diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index fca4c2675..d55776df0 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -1,5 +1,7 @@ package prog8.code +import prog8.code.ast.PtNode +import prog8.code.ast.PtProgram import prog8.code.core.* @@ -7,7 +9,7 @@ import prog8.code.core.* * Tree structure containing all symbol definitions in the program * (blocks, subroutines, variables (all types), memoryslabs, and labels). */ -class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) { +class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) { /** * The table as a flat mapping of scoped names to the StNode. * This gives the fastest lookup possible (no need to traverse tree nodes) @@ -76,7 +78,7 @@ enum class StNodeType { open class StNode(val name: String, val type: StNodeType, - val position: Position, + val astNode: PtNode, val children: MutableMap = mutableMapOf() ) { @@ -149,9 +151,9 @@ class StStaticVariable(name: String, val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments val onetimeInitializationStringValue: StString?, val onetimeInitializationArrayValue: StArray?, - val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte - val zpwish: ZeropageWish, - position: Position) : StNode(name, StNodeType.STATICVAR, position) { + val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte + val zpwish: ZeropageWish, // used in the variable allocator + astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) { init { if(bss) { @@ -180,8 +182,8 @@ class StStaticVariable(name: String, } -class StConstant(name: String, val dt: DataType, val value: Double, position: Position) : - StNode(name, StNodeType.CONSTANT, position) { +class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) : + StNode(name, StNodeType.CONSTANT, astNode) { } @@ -189,31 +191,31 @@ class StMemVar(name: String, val dt: DataType, val address: UInt, val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte - position: Position) : - StNode(name, StNodeType.MEMVAR, position) { + astNode: PtNode) : + StNode(name, StNodeType.MEMVAR, astNode) { } class StMemorySlab( name: String, val size: UInt, val align: UInt, - position: Position + astNode: PtNode ): - StNode(name, StNodeType.MEMORYSLAB, position) { + StNode(name, StNodeType.MEMORYSLAB, astNode) { } -class StSub(name: String, val parameters: List, val returnType: DataType?, position: Position) : - StNode(name, StNodeType.SUBROUTINE, position) { +class StSub(name: String, val parameters: List, val returnType: DataType?, astNode: PtNode) : + StNode(name, StNodeType.SUBROUTINE, astNode) { } class StRomSub(name: String, val address: UInt, val parameters: List, - val returns: List, - position: Position) : - StNode(name, StNodeType.ROMSUB, position) { -} + val returns: List, + astNode: PtNode) : + StNode(name, StNodeType.ROMSUB, astNode) + class StSubroutineParameter(val name: String, val type: DataType) diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt new file mode 100644 index 000000000..ad57077e1 --- /dev/null +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -0,0 +1,211 @@ +package prog8.code + +import prog8.code.ast.* +import prog8.code.core.* +import prog8.code.target.VMTarget +import java.util.* + +class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) { + fun make(): SymbolTable { + val st = SymbolTable(program) + + BuiltinFunctions.forEach { + st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY))) + } + + val scopestack = Stack() + scopestack.push(st) + program.children.forEach { + addToSt(it, scopestack) + } + require(scopestack.size==1) + + if(options.compTarget.name != VMTarget.NAME) { + listOf( + PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY), + PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY), + PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY), + PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY), + PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY), + PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY) + ).forEach { + it.parent = program + st.add(StMemVar(it.name, it.type, it.address, null, it)) + } + } + + return st + } + + private fun addToSt(node: PtNode, scope: Stack) { + val stNode = when(node) { + is PtAsmSub -> { + if(node.address==null) { + val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) } + StSub(node.name, params, node.returns.singleOrNull()?.second, node) + } else { + val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) } + val returns = node.returns.map { StRomSubParameter(it.first, it.second) } + StRomSub(node.name, node.address, parameters, returns, node) + } + } + is PtBlock -> { + StNode(node.name, StNodeType.BLOCK, node) + } + is PtConstant -> { + StConstant(node.name, node.type, node.value, node) + } + is PtLabel -> { + StNode(node.name, StNodeType.LABEL, node) + } + is PtMemMapped -> { + StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node) + } + is PtSub -> { + val params = node.parameters.map {StSubroutineParameter(it.name, it.type) } + StSub(node.name, params, node.returntype, node) + } + is PtVariable -> { + val bss = when (node.type) { + // TODO should bss be a computed property on PtVariable? + DataType.STR -> false + in ArrayDatatypes -> node.value==null || node.arraySize==0u + else -> node.value==null + } + val initialNumeric: Double? + val initialString: StString? + val initialArray: StArray? + val numElements: Int? + val value = node.value + if(value!=null) { + initialNumeric = (value as? PtNumber)?.number + when (value) { + is PtString -> { + initialString = StString(value.value, value.encoding) + initialArray = null + numElements = value.value.length + 1 // include the terminating 0-byte + } + is PtArray -> { + initialArray = makeInitialArray(value) + initialString = null + numElements = initialArray.size + require(node.arraySize?.toInt()==numElements) + } + else -> { + initialString = null + initialArray = null + numElements = node.arraySize?.toInt() + } + } + } else { + initialNumeric = null + initialArray = null + initialString = null + numElements = node.arraySize?.toInt() + } + StStaticVariable(node.name, node.type, bss, initialNumeric, initialString, initialArray, numElements, node.zeropage, node) + } + is PtBuiltinFunctionCall -> { + if(node.name=="memory") { + // memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable + require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} + val slabname = (node.args[0] as PtString).value + val size = (node.args[1] as PtNumber).number.toUInt() + val align = (node.args[2] as PtNumber).number.toUInt() + // don't add memory slabs in nested scope, just put them in the top level of the ST + scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node)) + } + null + } + else -> null // node is not present in the ST + } + + if(stNode!=null) { + scope.peek().add(stNode) + scope.push(stNode) + } + node.children.forEach { + addToSt(it, scope) + } + if(stNode!=null) + scope.pop() + } + + private fun makeInitialArray(value: PtArray): List { + return value.children.map { + when(it) { + is PtAddressOf -> StArrayElement(null, it.identifier.name) + is PtIdentifier -> StArrayElement(null, it.name) + is PtNumber -> StArrayElement(it.number, null) + else -> throw AssemblyError("invalid array element $it") + } + } + } + +} + +// override fun visit(decl: VarDecl) { +// val node = +// when(decl.type) { +// VarDeclType.VAR -> { +// var initialNumeric = (decl.value as? NumericLiteral)?.number +// if(initialNumeric==0.0) +// initialNumeric=null // variable will go into BSS and this will be set to 0 +// val initialStringLit = decl.value as? StringLiteral +// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding) +// val initialArrayLit = decl.value as? ArrayLiteral +// val initialArray = makeInitialArray(initialArrayLit) +// if(decl.isArray && decl.datatype !in ArrayDatatypes) +// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}") +// val numElements = +// if(decl.isArray) +// decl.arraysize!!.constIndex() +// else if(initialStringLit!=null) +// initialStringLit.value.length+1 // include the terminating 0-byte +// else +// null +// val bss = if(decl.datatype==DataType.STR) +// false +// else if(decl.isArray) +// initialArray.isNullOrEmpty() +// else +// initialNumeric == null +// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) +// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position) +// } +// VarDeclType.CONST -> { +// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) +// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position) +// } +// VarDeclType.MEMORY -> { +// val numElements = +// if(decl.datatype in ArrayDatatypes) +// decl.arraysize!!.constIndex() +// else null +// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) +// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position) +// } +// } +// scopestack.peek().add(node) +// // st.origAstLinks[decl] = node +// } +// +// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? { +// if(arrayLit==null) +// return null +// return arrayLit.value.map { +// when(it){ +// is AddressOf -> { +// val scopedName = it.identifier.targetNameAndType(program).first +// StArrayElement(null, scopedName) +// } +// is IdentifierReference -> { +// val scopedName = it.targetNameAndType(program).first +// StArrayElement(null, scopedName) +// } +// is NumericLiteral -> StArrayElement(it.number, null) +// else -> throw FatalAstException("weird element dt in array literal") +// } +// }.toList() +// } +// \ No newline at end of file diff --git a/codeCore/src/prog8/code/ast/AstBase.kt b/codeCore/src/prog8/code/ast/AstBase.kt index 0e3726918..457d32f58 100644 --- a/codeCore/src/prog8/code/ast/AstBase.kt +++ b/codeCore/src/prog8/code/ast/AstBase.kt @@ -3,6 +3,7 @@ package prog8.code.ast import prog8.code.core.IMemSizer import prog8.code.core.IStringEncoding import prog8.code.core.Position +import prog8.code.core.SourceCode import java.nio.file.Path // New simplified AST for the code generator. @@ -13,16 +14,6 @@ sealed class PtNode(val position: Position) { val children = mutableListOf() lateinit var parent: PtNode - fun printIndented(indent: Int) { - print(" ".repeat(indent)) - print("${this.javaClass.simpleName} ") - printProperties() - println() - children.forEach { it.printIndented(indent+1) } - } - - abstract fun printProperties() - fun add(child: PtNode) { children.add(child) child.parent = this @@ -36,12 +27,11 @@ sealed class PtNode(val position: Position) { fun definingBlock() = findParentNode(this) fun definingSub() = findParentNode(this) fun definingAsmSub() = findParentNode(this) + fun definingISub() = findParentNode(this) } -class PtNodeGroup : PtNode(Position.DUMMY) { - override fun printProperties() {} -} +class PtNodeGroup : PtNode(Position.DUMMY) sealed class PtNamedNode(var name: String, position: Position): PtNode(position) { @@ -65,10 +55,6 @@ class PtProgram( val memsizer: IMemSizer, val encoding: IStringEncoding ) : PtNode(Position.DUMMY) { - fun print() = printIndented(0) - override fun printProperties() { - print("'$name'") - } // fun allModuleDirectives(): Sequence = // children.asSequence().flatMap { it.children }.filterIsInstance().distinct() @@ -86,12 +72,9 @@ class PtBlock(name: String, val library: Boolean, val forceOutput: Boolean, val alignment: BlockAlignment, + val source: SourceCode, // taken from the module the block is defined in. position: Position ) : PtNamedNode(name, position) { - override fun printProperties() { - print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment") - } - enum class BlockAlignment { NONE, WORD, @@ -101,8 +84,6 @@ class PtBlock(name: String, class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) { - override fun printProperties() {} - init { require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" } require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" } @@ -110,28 +91,16 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi } -class PtLabel(name: String, position: Position) : PtNamedNode(name, position) { - override fun printProperties() { - print(name) - } -} +class PtLabel(name: String, position: Position) : PtNamedNode(name, position) -class PtBreakpoint(position: Position): PtNode(position) { - override fun printProperties() {} -} +class PtBreakpoint(position: Position): PtNode(position) -class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) { - override fun printProperties() { - print("filename=$file offset=$offset length=$length") - } -} +class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) -class PtNop(position: Position): PtNode(position) { - override fun printProperties() {} -} +class PtNop(position: Position): PtNode(position) // find the parent node of a specific type or interface diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index 1e1cbe084..93fcf43af 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -1,8 +1,6 @@ package prog8.code.ast -import prog8.code.core.DataType -import prog8.code.core.Encoding -import prog8.code.core.Position +import prog8.code.core.* import java.util.* import kotlin.math.round @@ -23,10 +21,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit } } - override fun printProperties() { - print(type) - } - infix fun isSameAs(other: PtExpression): Boolean { return when(this) { is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier @@ -43,6 +37,21 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit else -> false } } + + infix fun isSameAs(target: PtAssignTarget): Boolean { + return when { + target.memory != null && this is PtMemoryByte-> { + target.memory!!.address isSameAs this.address + } + target.identifier != null && this is PtIdentifier -> { + this.name == target.identifier!!.name + } + target.array != null && this is PtArrayIndexer -> { + this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index + } + else -> false + } + } } class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { @@ -51,11 +60,15 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { } -class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) { +class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) { val variable: PtIdentifier get() = children[0] as PtIdentifier val index: PtExpression get() = children[1] as PtExpression + + init { + require(elementType in NumericDatatypes) + } } @@ -66,6 +79,9 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position) return false return type==other.type && children == other.children } + + val size: Int + get() = children.size } @@ -81,9 +97,6 @@ class PtBuiltinFunctionCall(val name: String, val args: List get() = children.map { it as PtExpression } - override fun printProperties() { - print("$name void=$void noSideFx=$hasNoSideEffects") - } } @@ -93,10 +106,6 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio get() = children[0] as PtExpression val right: PtExpression get() = children[1] as PtExpression - - override fun printProperties() { - print("$operator -> $type") - } } @@ -119,28 +128,25 @@ class PtFunctionCall(val name: String, val args: List get() = children.map { it as PtExpression } - override fun printProperties() { - print("$name void=$void") - } } -class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) { - override fun printProperties() { - print("$name $type") - } -} +class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) { val address: PtExpression get() = children.single() as PtExpression - override fun printProperties() {} } class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { + companion object { + fun fromBoolean(bool: Boolean, position: Position): PtNumber = + PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position) + } + init { if(type==DataType.BOOL) throw IllegalArgumentException("bool should have become ubyte @$position") @@ -151,10 +157,6 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre } } - override fun printProperties() { - print("$number ($type)") - } - override fun hashCode(): Int = Objects.hash(type, number) override fun equals(other: Any?): Boolean { @@ -175,10 +177,6 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr // note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0 require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" } } - - override fun printProperties() { - print(operator) - } } @@ -189,16 +187,10 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position) get() = children[1] as PtExpression val step: PtNumber get() = children[2] as PtNumber - - override fun printProperties() {} } class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) { - override fun printProperties() { - print("$encoding:\"$value\"") - } - override fun hashCode(): Int = Objects.hash(value, encoding) override fun equals(other: Any?): Boolean { if(other==null || other !is PtString) @@ -214,12 +206,8 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi } -// special node that isn't created from compiling user code, but used internally -class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) { - override fun printProperties() { - print("reg=$register $type") - } -} +// special node that isn't created from compiling user code, but used internally in the Intermediate Code +class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null diff --git a/codeCore/src/prog8/code/ast/AstPrinter.kt b/codeCore/src/prog8/code/ast/AstPrinter.kt index 0e706dde8..d8b8717da 100644 --- a/codeCore/src/prog8/code/ast/AstPrinter.kt +++ b/codeCore/src/prog8/code/ast/AstPrinter.kt @@ -57,7 +57,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { is PtAsmSub -> { val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..." val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}" - val returns = if (node.returnTypes.isEmpty()) "" else (if (node.returnTypes.size == 1) "-> ${node.returnTypes[0].name.lowercase()}" else "-> ${node.returnTypes.map { it.name.lowercase() }}") + val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}") val str = if (node.inline) "inline " else "" if(node.address==null) { str + "asmsub ${node.name}($params) $clobbers $returns" @@ -66,7 +66,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { } } is PtBlock -> { - val addr = if(node.address==null) "" else "@${node.address?.toHex()}" + val addr = if(node.address==null) "" else "@${node.address.toHex()}" val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}" "\nblock '${node.name}' $addr $align" } @@ -86,8 +86,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { } is PtSub -> { val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..." - var str = if(node.inline) "inline " else "" - str += "sub ${node.name}($params) " + var str = "sub ${node.name}($params) " if(node.returntype!=null) str += "-> ${node.returntype.name.lowercase()}" str @@ -104,7 +103,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { else "${node.type.name.lowercase()} ${node.name}" if(node.value!=null) - str + " = " + txt(node.value!!) + str + " = " + txt(node.value) else str } @@ -135,6 +134,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { output(" ".repeat(depth) + txt(node)) } } + println() } else { walkAst(root) { node, depth -> val txt = txt(node) diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 3b2819191..8f9c5f440 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -3,48 +3,39 @@ package prog8.code.ast import prog8.code.core.* +sealed interface IPtSubroutine { + val name: String +} + class PtAsmSub( name: String, val address: UInt?, val clobbers: Set, - val parameters: List>, - val returnTypes: List, // TODO join with register as Pairs ? - val retvalRegisters: List, + val parameters: List>, + val returns: List>, val inline: Boolean, position: Position -) : PtNamedNode(name, position) { - override fun printProperties() { - print("$name inline=$inline") - } -} +) : PtNamedNode(name, position), IPtSubroutine class PtSub( name: String, val parameters: List, val returntype: DataType?, - val inline: Boolean, position: Position -) : PtNamedNode(name, position) { - override fun printProperties() { - print(name) - } - +) : PtNamedNode(name, position), IPtSubroutine { init { // params and return value should not be str if(parameters.any{ it.type !in NumericDatatypes }) throw AssemblyError("non-numeric parameter") if(returntype!=null && returntype !in NumericDatatypes) throw AssemblyError("non-numeric returntype $returntype") + parameters.forEach { it.parent=this } } } -class PtSubroutineParameter(val name: String, val type: DataType, position: Position): PtNode(position) { - override fun printProperties() { - print("$type $name") - } -} +class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position) class PtAssignment(position: Position) : PtNode(position) { @@ -53,8 +44,6 @@ class PtAssignment(position: Position) : PtNode(position) { val value: PtExpression get() = children[1] as PtExpression - override fun printProperties() { } - val isInplaceAssign: Boolean by lazy { val target = target.children.single() as PtExpression when(val source = value) { @@ -102,7 +91,7 @@ class PtAssignTarget(position: Position) : PtNode(position) { } } - override fun printProperties() {} + infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this) } @@ -111,10 +100,6 @@ class PtConditionalBranch(val condition: BranchCondition, position: Position) : get() = children[0] as PtNodeGroup val falseScope: PtNodeGroup get() = children[1] as PtNodeGroup - - override fun printProperties() { - print(condition) - } } @@ -125,8 +110,6 @@ class PtForLoop(position: Position) : PtNode(position) { get() = children[1] as PtExpression val statements: PtNodeGroup get() = children[2] as PtNodeGroup - - override fun printProperties() {} } @@ -137,8 +120,6 @@ class PtIfElse(position: Position) : PtNode(position) { get() = children[1] as PtNodeGroup val elseScope: PtNodeGroup get() = children[2] as PtNodeGroup - - override fun printProperties() {} } @@ -146,10 +127,8 @@ class PtJump(val identifier: PtIdentifier?, val address: UInt?, val generatedLabel: String?, position: Position) : PtNode(position) { - override fun printProperties() { - identifier?.printProperties() - if(address!=null) print(address.toHex()) - if(generatedLabel!=null) print(generatedLabel) + init { + identifier?.let {it.parent = this } } } @@ -157,10 +136,6 @@ class PtJump(val identifier: PtIdentifier?, class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) { val target: PtAssignTarget get() = children.single() as PtAssignTarget - - override fun printProperties() { - print(operator) - } } @@ -169,8 +144,6 @@ class PtRepeatLoop(position: Position) : PtNode(position) { get() = children[0] as PtExpression val statements: PtNodeGroup get() = children[1] as PtNodeGroup - - override fun printProperties() {} } @@ -183,30 +156,26 @@ class PtReturn(position: Position) : PtNode(position) { else null } - - override fun printProperties() {} } -class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) { - override fun printProperties() { - print("$type $name") +sealed interface IPtVariable { + val name: String + val type: DataType +} + + +class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable { + init { + value?.let {it.parent=this} } } -class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) { - override fun printProperties() { - print("$type $name = $value") - } -} +class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable -class PtMemMapped(name: String, val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position) { - override fun printProperties() { - print("&$type $name = ${address.toHex()}") - } -} +class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable class PtWhen(position: Position) : PtNode(position) { @@ -214,8 +183,6 @@ class PtWhen(position: Position) : PtNode(position) { get() = children[0] as PtExpression val choices: PtNodeGroup get() = children[1] as PtNodeGroup - - override fun printProperties() {} } @@ -224,5 +191,4 @@ class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) { get() = children[0] as PtNodeGroup val statements: PtNodeGroup get() = children[1] as PtNodeGroup - override fun printProperties() {} } diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt new file mode 100644 index 000000000..1f924634e --- /dev/null +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -0,0 +1,111 @@ +package prog8.code.core + +class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean) +class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean) +class CallConvention(val params: List, val returns: ReturnConvention) { + override fun toString(): String { + val paramConvs = params.mapIndexed { index, it -> + when { + it.reg!=null -> "$index:${it.reg}" + it.variable -> "$index:variable" + else -> "$index:???" + } + } + val returnConv = + when { + returns.reg!=null -> returns.reg.toString() + returns.floatFac1 -> "floatFAC1" + else -> "" + } + return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" + } +} + +class FParam(val name: String, val possibleDatatypes: Array) + +class FSignature(val pure: Boolean, // does it have side effects? + val parameters: List, + val returnType: DataType?) { + + fun callConvention(actualParamTypes: List): CallConvention { + val returns: ReturnConvention = when (returnType) { + DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) + DataType.FLOAT -> ReturnConvention(returnType, null, true) + in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) + null -> ReturnConvention(null, null, false) + else -> { + // return type depends on arg type + when (val paramType = actualParamTypes.first()) { + DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) + DataType.FLOAT -> ReturnConvention(paramType, null, true) + in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) + else -> ReturnConvention(paramType, null, false) + } + } + } + + return when { + actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns) + actualParamTypes.size==1 -> { + // one parameter goes via register/registerpair + val paramConv = when(val paramType = actualParamTypes[0]) { + DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) + DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) + in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false) + else -> ParamConvention(paramType, null, false) + } + CallConvention(listOf(paramConv), returns) + } + else -> { + // multiple parameters go via variables + val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } + CallConvention(paramConvs, returns) + } + } + } +} + + +val BuiltinFunctions: Map = mapOf( + // this set of function have no return value and operate in-place: + "rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + "ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + "rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + "ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + "sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), + "reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), + // cmp returns a status in the carry flag, but not a proper return value + "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), + "abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD), + "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), + // normal functions follow: + "sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE), + "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE), + "sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), + "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), + "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), + "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), + "msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), + "mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), + "peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), + "peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), + "poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), + "pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), + "pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), + "pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null), + "popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null), + "push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null), + "pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null), + "rsave" to FSignature(false, emptyList(), null), + "rsavex" to FSignature(false, emptyList(), null), + "rrestore" to FSignature(false, emptyList(), null), + "rrestorex" to FSignature(false, emptyList(), null), + "memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), + "callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), + "callrom" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), +) + +val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse") diff --git a/codeCore/src/prog8/code/core/CompilationOptions.kt b/codeCore/src/prog8/code/core/CompilationOptions.kt index 5b5d8f08c..2a12fd878 100644 --- a/codeCore/src/prog8/code/core/CompilationOptions.kt +++ b/codeCore/src/prog8/code/core/CompilationOptions.kt @@ -23,4 +23,8 @@ class CompilationOptions(val output: OutputType, var evalStackBaseAddress: UInt? = null, var outputDir: Path = Path(""), var symbolDefs: Map = emptyMap() -) +) { + init { + compTarget.machine.initializeMemoryAreas(this) + } +} diff --git a/codeCore/src/prog8/code/core/IAssemblyGenerator.kt b/codeCore/src/prog8/code/core/IAssemblyGenerator.kt deleted file mode 100644 index 544f78f04..000000000 --- a/codeCore/src/prog8/code/core/IAssemblyGenerator.kt +++ /dev/null @@ -1,12 +0,0 @@ -package prog8.code.core - -interface IAssemblyGenerator { - fun compileToAssembly(): IAssemblyProgram? -} - -interface IAssemblyProgram { - val name: String - fun assemble(options: CompilationOptions): Boolean -} - -fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" diff --git a/codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt b/codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt new file mode 100644 index 000000000..433395d18 --- /dev/null +++ b/codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt @@ -0,0 +1,19 @@ +package prog8.code.core + +import prog8.code.SymbolTable +import prog8.code.ast.PtProgram + +interface ICodeGeneratorBackend { + fun generate(program: PtProgram, + symbolTable: SymbolTable, + options: CompilationOptions, + errors: IErrorReporter): IAssemblyProgram? +} + + +interface IAssemblyProgram { + val name: String + fun assemble(options: CompilationOptions): Boolean +} + +fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" diff --git a/codeCore/src/prog8/code/core/ICompilationTarget.kt b/codeCore/src/prog8/code/core/ICompilationTarget.kt index 89f67edeb..865628139 100644 --- a/codeCore/src/prog8/code/core/ICompilationTarget.kt +++ b/codeCore/src/prog8/code/core/ICompilationTarget.kt @@ -7,5 +7,5 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { val defaultEncoding: Encoding override fun encodeString(str: String, encoding: Encoding): List - override fun decodeString(bytes: List, encoding: Encoding): String + override fun decodeString(bytes: Iterable, encoding: Encoding): String } diff --git a/codeCore/src/prog8/code/core/IStringEncoding.kt b/codeCore/src/prog8/code/core/IStringEncoding.kt index 95e9d723f..c10393ef0 100644 --- a/codeCore/src/prog8/code/core/IStringEncoding.kt +++ b/codeCore/src/prog8/code/core/IStringEncoding.kt @@ -10,5 +10,5 @@ enum class Encoding(val prefix: String) { interface IStringEncoding { fun encodeString(str: String, encoding: Encoding): List - fun decodeString(bytes: List, encoding: Encoding): String + fun decodeString(bytes: Iterable, encoding: Encoding): String } diff --git a/codeCore/src/prog8/code/target/Encoder.kt b/codeCore/src/prog8/code/target/Encoder.kt index fb3c967c2..5de4f0920 100644 --- a/codeCore/src/prog8/code/target/Encoder.kt +++ b/codeCore/src/prog8/code/target/Encoder.kt @@ -23,7 +23,7 @@ object Encoder: IStringEncoding { success = { it } ) } - override fun decodeString(bytes: List, encoding: Encoding): String { + override fun decodeString(bytes: Iterable, encoding: Encoding): String { val decoded = when(encoding) { Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) diff --git a/codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt b/codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt index 633978d6d..51f5d3173 100644 --- a/codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt +++ b/codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt @@ -208,7 +208,7 @@ object AtasciiEncoding { return Ok(mapped) } - fun decode(bytes: List): Result { + fun decode(bytes: Iterable): Result { return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString("")) } } diff --git a/codeCore/src/prog8/code/target/cbm/IsoEncoding.kt b/codeCore/src/prog8/code/target/cbm/IsoEncoding.kt index 8d427c162..acd12e849 100644 --- a/codeCore/src/prog8/code/target/cbm/IsoEncoding.kt +++ b/codeCore/src/prog8/code/target/cbm/IsoEncoding.kt @@ -27,7 +27,7 @@ object IsoEncoding { } } - fun decode(bytes: List): Result { + fun decode(bytes: Iterable): Result { return try { Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) } catch (ce: CharConversionException) { diff --git a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt index c4e0f144c..ab242ea68 100644 --- a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt @@ -44,9 +44,24 @@ class VirtualMachineDefinition: IMachineDefinition { override fun isIOAddress(address: UInt): Boolean = false - override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {} + override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { + zeropage = VirtualZeropage(compilerOptions) + } } interface IVirtualMachineRunner { fun runProgram(irSource: String) } + +private class VirtualZeropage(options: CompilationOptions): Zeropage(options) { + override val SCRATCH_B1: UInt + get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") + override val SCRATCH_REG: UInt + get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") + override val SCRATCH_W1: UInt + get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") + override val SCRATCH_W2: UInt + get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") + + override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ } +} diff --git a/codeGenCpu6502/build.gradle b/codeGenCpu6502/build.gradle index 4d3442e25..31390f470 100644 --- a/codeGenCpu6502/build.gradle +++ b/codeGenCpu6502/build.gradle @@ -3,6 +3,7 @@ plugins { id 'java' id 'application' id "org.jetbrains.kotlin.jvm" + id "io.kotest" version "0.3.9" } java { @@ -25,11 +26,11 @@ compileTestKotlin { dependencies { implementation project(':codeCore') - implementation project(':compilerAst') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" // implementation "org.jetbrains.kotlin:kotlin-reflect" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" + testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2' } sourceSets { @@ -41,6 +42,22 @@ sourceSets { srcDirs = ["${project.projectDir}/res"] } } + test { + java { + srcDir "${project.projectDir}/test" + } + } } -// note: there are no unit tests in this module! +test { + // Enable JUnit 5 (Gradle 4.6+). + useJUnitPlatform() + + // Always run tests, even when nothing changed. + dependsOn 'cleanTest' + + // Show test results. + testLogging { + events "skipped", "failed" + } +} diff --git a/codeGenCpu6502/codeGenCpu6502.iml b/codeGenCpu6502/codeGenCpu6502.iml index 4e52dfb59..7061784ab 100644 --- a/codeGenCpu6502/codeGenCpu6502.iml +++ b/codeGenCpu6502/codeGenCpu6502.iml @@ -4,13 +4,15 @@ + - + + \ No newline at end of file diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index fe9bf79fd..4904a02a6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -1,18 +1,11 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold -import prog8.ast.IFunctionCall -import prog8.ast.Node -import prog8.ast.ParentSentinel -import prog8.ast.Program -import prog8.ast.base.FatalAstException -import prog8.ast.expressions.* -import prog8.ast.statements.* +import prog8.code.StNodeType import prog8.code.SymbolTable +import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.assignment.* -import prog8.compiler.BuiltinFunctions -import prog8.compiler.builtinFunctionReturnType import java.util.* import kotlin.io.path.Path import kotlin.io.path.writeLines @@ -21,12 +14,24 @@ import kotlin.io.path.writeLines internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" +class AsmGen6502: ICodeGeneratorBackend { + override fun generate( + program: PtProgram, + symbolTable: SymbolTable, + options: CompilationOptions, + errors: IErrorReporter + ): IAssemblyProgram? { + val asmgen = AsmGen6502Internal(program, symbolTable, options, errors) + return asmgen.compileToAssembly() + } +} -class AsmGen(internal val program: Program, - internal val symbolTable: SymbolTable, - internal val options: CompilationOptions, - internal val errors: IErrorReporter -): IAssemblyGenerator { +class AsmGen6502Internal ( + val program: PtProgram, + internal val symbolTable: SymbolTable, + internal val options: CompilationOptions, + internal val errors: IErrorReporter +) { internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100) internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640) @@ -43,7 +48,7 @@ class AsmGen(internal val program: Program, private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) - override fun compileToAssembly(): IAssemblyProgram? { + fun compileToAssembly(): IAssemblyProgram? { assemblyLines.clear() loopEndLabels.clear() @@ -56,7 +61,7 @@ class AsmGen(internal val program: Program, if(options.optimize) { val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') } assemblyLines.clear() - while(optimizeAssembly(separateLines, options.compTarget.machine, program)>0) { + while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) { // optimize the assembly source code } output.writeLines(separateLines) @@ -72,7 +77,7 @@ class AsmGen(internal val program: Program, internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu - internal fun outputSourceLine(node: Node) { + internal fun outputSourceLine(node: PtNode) { out(" ;\tsrc line: ${node.position.file}:${node.position.line}") } @@ -97,8 +102,27 @@ class AsmGen(internal val program: Program, fun asmVariableName(name: String) = fixNameSymbols(name) fun asmSymbolName(name: Iterable) = fixNameSymbols(name.joinToString(".")) fun asmVariableName(name: Iterable) = fixNameSymbols(name.joinToString(".")) - fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource) - fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource) + fun asmSymbolName(identifier: PtIdentifier): String { + val name = asmSymbolName(identifier.name) + + // see if we're inside a subroutine, if so, remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block) + val subName = identifier.definingSub()?.scopedName + return if (subName != null && name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.') + name.drop(subName.length + 1) + else + name + } + + fun asmVariableName(identifier: PtIdentifier): String { + val name = asmVariableName(identifier.name) + + // see if we're inside a subroutine, if so, remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block) + val subName = identifier.definingSub()?.scopedName + return if (subName != null && name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.') + name.drop(subName.length+1) + else + name + } internal fun getTempVarName(dt: DataType): String { return when(dt) { @@ -107,23 +131,24 @@ class AsmGen(internal val program: Program, DataType.UWORD -> "cx16.r9" DataType.WORD -> "cx16.r9s" DataType.FLOAT -> TODO("no temporary float var available") - else -> throw FatalAstException("invalid dt $dt") + else -> throw AssemblyError("invalid dt $dt") } } - internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): String { + internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String { // returns the source name of the zero page pointervar if it's already in the ZP, // otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary - when (val target = pointervar.targetStatement(program)) { - is Label -> { + val symbol = symbolTable.lookup(pointervar.name) + when (val target = symbol!!.astNode) { + is PtLabel -> { val sourceName = asmSymbolName(pointervar) out(" lda $sourceName") return sourceName } - is VarDecl -> { + is PtVariable, is PtMemMapped -> { val sourceName = asmVariableName(pointervar) if (isTargetCpu(CpuType.CPU65c02)) { - return if (allocator.isZpVar(target.scopedName)) { + return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string // pointervar is already in the zero page, no need to copy out(" lda ($sourceName)") sourceName @@ -137,7 +162,7 @@ class AsmGen(internal val program: Program, "P8ZP_SCRATCH_W1" } } else { - return if (allocator.isZpVar(target.scopedName)) { + return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string // pointervar is already in the zero page, no need to copy out(" ldy #0 | lda ($sourceName),y") sourceName @@ -153,15 +178,14 @@ class AsmGen(internal val program: Program, } } } - else -> throw AssemblyError("invalid pointervar") + else -> throw AssemblyError("invalid pointervar $target") } } - internal fun storeAIntoPointerVar(pointervar: IdentifierReference) { + internal fun storeAIntoPointerVar(pointervar: PtIdentifier) { val sourceName = asmVariableName(pointervar) - val vardecl = pointervar.targetVarDecl(program)!! if (isTargetCpu(CpuType.CPU65c02)) { - if (allocator.isZpVar(vardecl.scopedName)) { + if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string // pointervar is already in the zero page, no need to copy out(" sta ($sourceName)") } else { @@ -173,7 +197,7 @@ class AsmGen(internal val program: Program, sta (P8ZP_SCRATCH_W2)""") } } else { - if (allocator.isZpVar(vardecl.scopedName)) { + if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string // pointervar is already in the zero page, no need to copy out(" ldy #0 | sta ($sourceName),y") } else { @@ -207,7 +231,7 @@ class AsmGen(internal val program: Program, return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly } - internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) { + internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) { if (isTargetCpu(CpuType.CPU65c02)) { // just use the cpu's stack for all registers, shorter code when (register) { @@ -303,54 +327,53 @@ class AsmGen(internal val program: Program, } } - internal fun translate(stmt: Statement) { + internal fun translate(stmt: PtNode) { outputSourceLine(stmt) when(stmt) { - is Directive -> translate(stmt) - is Return -> translate(stmt) - is Subroutine -> programGen.translateSubroutine(stmt) - is InlineAssembly -> translate(stmt) - is BuiltinFunctionCallStatement -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt) - is FunctionCallStatement -> functioncallAsmGen.translateFunctionCallStatement(stmt) - is Assignment -> assignmentAsmGen.translate(stmt) - is Jump -> { + is PtReturn -> translate(stmt) + is PtSub -> programGen.translateSubroutine(stmt) + is PtAsmSub -> programGen.translateAsmSubroutine(stmt) + is PtInlineAssembly -> translate(stmt) + is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt) + is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt) + is PtAssignment -> assignmentAsmGen.translate(stmt) + is PtJump -> { val (asmLabel, indirect) = getJumpTarget(stmt) jmp(asmLabel, indirect) } - is PostIncrDecr -> postincrdecrAsmGen.translate(stmt) - is Label -> translate(stmt) - is ConditionalBranch -> translate(stmt) - is IfElse -> translate(stmt) - is ForLoop -> forloopsAsmGen.translate(stmt) - is RepeatLoop -> translate(stmt) - is When -> translate(stmt) - is AnonymousScope -> translate(stmt) - is VarDecl -> { /* do nothing; variables are handled elsewhere */ } - is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore") - is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps") - is WhileLoop -> throw AssemblyError("while should have been converted to jumps") - is Block -> throw AssemblyError("block should have been handled elsewhere") - is Break -> throw AssemblyError("break should have been replaced by goto") + is PtPostIncrDecr -> postincrdecrAsmGen.translate(stmt) + is PtLabel -> translate(stmt) + is PtConditionalBranch -> translate(stmt) + is PtIfElse -> translate(stmt) + is PtForLoop -> forloopsAsmGen.translate(stmt) + is PtRepeatLoop -> translate(stmt) + is PtWhen -> translate(stmt) + is PtIncludeBinary -> translate(stmt) + is PtBreakpoint -> translate(stmt) + is PtVariable, is PtConstant, is PtMemMapped -> { /* do nothing; variables are handled elsewhere */ } + is PtBlock -> throw AssemblyError("block should have been handled elsewhere") + is PtNodeGroup -> stmt.children.forEach { translate(it) } + is PtNop -> {} else -> throw AssemblyError("missing asm translation for $stmt") } } internal fun loadScaledArrayIndexIntoRegister( - expr: ArrayIndexedExpression, + expr: PtArrayIndexer, elementDt: DataType, register: CpuRegister, addOneExtra: Boolean = false ) { val reg = register.toString().lowercase() - val indexnum = expr.indexer.constIndex() + val indexnum = expr.index.asConstInteger() if (indexnum != null) { val indexValue = indexnum * options.compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0 out(" ld$reg #$indexValue") return } - val indexVar = expr.indexer.indexExpr as? IdentifierReference - ?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}") + val indexVar = expr.index as? PtIdentifier + ?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}") val indexName = asmVariableName(indexVar) if (addOneExtra) { @@ -428,28 +451,28 @@ class AsmGen(internal val program: Program, } @Deprecated("avoid calling this as it generates slow evalstack based code") - internal fun translateExpression(expression: Expression) = + internal fun translateExpression(expression: PtExpression) = expressionsAsmGen.translateExpression(expression) - internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) = + internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? = builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister) - internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) = - functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression) + internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) = + functioncallAsmGen.translateFunctionCall(functionCallExpr) - internal fun saveXbeforeCall(functionCall: IFunctionCall) = + internal fun saveXbeforeCall(functionCall: PtFunctionCall) = functioncallAsmGen.saveXbeforeCall(functionCall) - internal fun restoreXafterCall(functionCall: IFunctionCall) = + internal fun restoreXafterCall(functionCall: PtFunctionCall) = functioncallAsmGen.restoreXafterCall(functionCall) internal fun translateNormalAssignment(assign: AsmAssignment) = assignmentAsmGen.translateNormalAssignment(assign) - internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean=false) = + internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean=false) = assignmentAsmGen.assignExpressionToRegister(expr, register, signed) - internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) = + internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) = assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope) internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean=false) = @@ -470,7 +493,7 @@ class AsmGen(internal val program: Program, } } - internal fun assignExpressionTo(value: Expression, target: AsmAssignTarget) { + internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) { // don't use translateExpression() to avoid evalstack when (target.datatype) { in ByteDatatypes -> { @@ -519,59 +542,59 @@ class AsmGen(internal val program: Program, } } - private fun translate(stmt: IfElse) { + private fun translate(stmt: PtIfElse) { requireComparisonExpression(stmt.condition) // IfStatement: condition must be of form 'x ' - val booleanCondition = stmt.condition as BinaryExpression + val booleanCondition = stmt.condition - if (stmt.elsepart.isEmpty()) { - val jump = stmt.truepart.statements.singleOrNull() - if(jump is Jump) { + if (stmt.elseScope.children.isEmpty()) { + val jump = stmt.ifScope.children.singleOrNull() + if(jump is PtJump) { translateCompareAndJumpIfTrue(booleanCondition, jump) } else { - val endLabel = program.makeLabel("if_end") + val endLabel = makeLabel("if_end") translateCompareAndJumpIfFalse(booleanCondition, endLabel) - translate(stmt.truepart) + translate(stmt.ifScope) out(endLabel) } } else { // both true and else parts - val elseLabel = program.makeLabel("if_else") - val endLabel = program.makeLabel("if_end") + val elseLabel = makeLabel("if_else") + val endLabel = makeLabel("if_end") translateCompareAndJumpIfFalse(booleanCondition, elseLabel) - translate(stmt.truepart) + translate(stmt.ifScope) jmp(endLabel) out(elseLabel) - translate(stmt.elsepart) + translate(stmt.elseScope) out(endLabel) } } - private fun requireComparisonExpression(condition: Expression) { - if(condition !is BinaryExpression || condition.operator !in ComparisonOperators) + private fun requireComparisonExpression(condition: PtExpression) { + if(condition !is PtBinaryExpression || condition.operator !in ComparisonOperators) throw AssemblyError("expected boolean comparison expression $condition") } - private fun translate(stmt: RepeatLoop) { - val endLabel = program.makeLabel("repeatend") + private fun translate(stmt: PtRepeatLoop) { + val endLabel = makeLabel("repeatend") loopEndLabels.push(endLabel) - when (stmt.iterations) { - null -> throw AssemblyError("repeat-forever loop should have been replaced by label+jump") - is NumericLiteral -> { - val iterations = (stmt.iterations as NumericLiteral).number.toInt() + when (stmt.count) { + is PtNumber -> { + val iterations = (stmt.count as PtNumber).number.toInt() when { iterations == 0 -> {} - iterations == 1 -> translate(stmt.body) + iterations == 1 -> translate(stmt.statements) iterations<0 || iterations>65535 -> throw AssemblyError("invalid number of iterations") iterations <= 256 -> repeatByteCount(iterations, stmt) else -> repeatWordCount(iterations, stmt) } } - is IdentifierReference -> { - val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program) as VarDecl - val name = asmVariableName(stmt.iterations as IdentifierReference) - when(vardecl.datatype) { + is PtIdentifier -> { + val symbol = symbolTable.lookup((stmt.count as PtIdentifier).name) + val vardecl = symbol!!.astNode as IPtVariable + val name = asmVariableName(stmt.count as PtIdentifier) + when(vardecl.type) { DataType.UBYTE, DataType.BYTE -> { assignVariableToRegister(name, RegisterOrPair.Y) repeatCountInY(stmt, endLabel) @@ -584,19 +607,16 @@ class AsmGen(internal val program: Program, } } else -> { - val dt = stmt.iterations!!.inferType(program) - if(!dt.isKnown) - throw AssemblyError("unknown dt") - when (dt.getOr(DataType.UNDEFINED)) { + when (stmt.count.type) { in ByteDatatypes -> { - assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.Y) + assignExpressionToRegister(stmt.count, RegisterOrPair.Y) repeatCountInY(stmt, endLabel) } in WordDatatypes -> { - assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.AY) + assignExpressionToRegister(stmt.count, RegisterOrPair.AY) repeatWordCountInAY(endLabel, stmt) } - else -> throw AssemblyError("invalid loop expression datatype $dt") + else -> throw AssemblyError("invalid loop expression datatype ${stmt.count.type}") } } } @@ -604,9 +624,9 @@ class AsmGen(internal val program: Program, loopEndLabels.pop() } - private fun repeatWordCount(count: Int, stmt: RepeatLoop) { + private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) { require(count in 257..65535) { "invalid repeat count ${stmt.position}" } - val repeatLabel = program.makeLabel("repeat") + val repeatLabel = makeLabel("repeat") if(isTargetCpu(CpuType.CPU65c02)) { val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt) out(""" @@ -615,7 +635,7 @@ class AsmGen(internal val program: Program, sta $counterVar sty $counterVar+1 $repeatLabel""") - translate(stmt.body) + translate(stmt.statements) out(""" lda $counterVar bne + @@ -632,7 +652,7 @@ $repeatLabel""") sta $counterVar sty $counterVar+1 $repeatLabel""") - translate(stmt.body) + translate(stmt.statements) out(""" lda $counterVar bne + @@ -644,10 +664,10 @@ $repeatLabel""") } } - private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) { + private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) { // note: A/Y must have been loaded with the number of iterations! // no need to explicitly test for 0 iterations as this is done in the countdown logic below - val repeatLabel = program.makeLabel("repeat") + val repeatLabel = makeLabel("repeat") val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt) out(""" sta $counterVar @@ -661,58 +681,58 @@ $repeatLabel lda $counterVar dec $counterVar+1 + dec $counterVar """) - translate(stmt.body) + translate(stmt.statements) jmp(repeatLabel) out(endLabel) } - private fun repeatByteCount(count: Int, stmt: RepeatLoop) { + private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) { require(count in 2..256) { "invalid repeat count ${stmt.position}" } - val repeatLabel = program.makeLabel("repeat") + val repeatLabel = makeLabel("repeat") if(isTargetCpu(CpuType.CPU65c02)) { val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt) out(" lda #${count and 255} | sta $counterVar") out(repeatLabel) - translate(stmt.body) + translate(stmt.statements) out(" dec $counterVar | bne $repeatLabel") } else { val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt) out(" lda #${count and 255} | sta $counterVar") out(repeatLabel) - translate(stmt.body) + translate(stmt.statements) out(" dec $counterVar | bne $repeatLabel") } } - private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) { + private fun repeatCountInY(stmt: PtRepeatLoop, endLabel: String) { // note: Y must just have been loaded with the (variable) number of loops to be performed! - val repeatLabel = program.makeLabel("repeat") + val repeatLabel = makeLabel("repeat") if(isTargetCpu(CpuType.CPU65c02)) { val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt) out(" beq $endLabel | sty $counterVar") out(repeatLabel) - translate(stmt.body) + translate(stmt.statements) out(" dec $counterVar | bne $repeatLabel") } else { val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt) out(" beq $endLabel | sty $counterVar") out(repeatLabel) - translate(stmt.body) + translate(stmt.statements) out(" dec $counterVar | bne $repeatLabel") } out(endLabel) } - private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: RepeatLoop): String { - val scope = stmt.definingSubroutine!! + private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String { + val scope = stmt.definingISub()!! val asmInfo = subroutineExtra(scope) var parent = stmt.parent - while(parent !is ParentSentinel) { - if(parent is RepeatLoop) + while(parent !is PtProgram) { + if(parent is PtRepeatLoop) break parent = parent.parent } - val isNested = parent is RepeatLoop + val isNested = parent is PtRepeatLoop if(!isNested) { // we can re-use a counter var from the subroutine if it already has one for that datatype @@ -723,7 +743,7 @@ $repeatLabel lda $counterVar } } - val counterVar = program.makeLabel("counter") + val counterVar = makeLabel("counter") when(dt) { DataType.UBYTE, DataType.UWORD -> { val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors) @@ -737,27 +757,25 @@ $repeatLabel lda $counterVar } } - private fun translate(stmt: When) { - val endLabel = program.makeLabel("choice_end") - val choiceBlocks = mutableListOf>() - val conditionDt = stmt.condition.inferType(program) - if(!conditionDt.isKnown) - throw AssemblyError("unknown condition dt") - if(conditionDt.getOr(DataType.BYTE) in ByteDatatypes) - assignExpressionToRegister(stmt.condition, RegisterOrPair.A) + private fun translate(stmt: PtWhen) { + val endLabel = makeLabel("choice_end") + val choiceBlocks = mutableListOf>() + val conditionDt = stmt.value.type + if(conditionDt in ByteDatatypes) + assignExpressionToRegister(stmt.value, RegisterOrPair.A) else - assignExpressionToRegister(stmt.condition, RegisterOrPair.AY) + assignExpressionToRegister(stmt.value, RegisterOrPair.AY) - for(choice in stmt.choices) { - val choiceLabel = program.makeLabel("choice") - if(choice.values==null) { - // the else choice + for(choiceNode in stmt.choices.children) { + val choice = choiceNode as PtWhenChoice + val choiceLabel = makeLabel("choice") + if(choice.isElse) { translate(choice.statements) } else { choiceBlocks.add(choiceLabel to choice.statements) - for (cv in choice.values!!) { - val value = (cv as NumericLiteral).number.toInt() - if(conditionDt.getOr(DataType.BYTE) in ByteDatatypes) { + for (cv in choice.values.children) { + val value = (cv as PtNumber).number.toInt() + if(conditionDt in ByteDatatypes) { out(" cmp #${value.toHex()} | beq $choiceLabel") } else { out(""" @@ -781,20 +799,15 @@ $repeatLabel lda $counterVar out(endLabel) } - private fun translate(stmt: Label) { + private fun translate(stmt: PtLabel) { out(stmt.name) } - private fun translate(scope: AnonymousScope) { - // note: the variables defined in an anonymous scope have been moved to their defining subroutine's scope - scope.statements.forEach{ translate(it) } - } - - private fun translate(stmt: ConditionalBranch) { - if(stmt.truepart.isEmpty() && stmt.elsepart.isNotEmpty()) + private fun translate(stmt: PtConditionalBranch) { + if(stmt.trueScope.children.isEmpty() && stmt.falseScope.children.isNotEmpty()) throw AssemblyError("only else part contains code, shoud have been switched already") - val jump = stmt.truepart.statements.firstOrNull() as? Jump + val jump = stmt.trueScope.children.firstOrNull() as? PtJump if(jump!=null) { // branch with only a jump (goto) val instruction = branchInstruction(stmt.condition, false) @@ -809,74 +822,40 @@ $repeatLabel lda $counterVar else { out(" $instruction $asmLabel") } - translate(stmt.elsepart) + translate(stmt.falseScope) } else { - if(stmt.elsepart.isEmpty()) { - if(stmt.truepart.isNotEmpty()) { + if(stmt.falseScope.children.isEmpty()) { + if(stmt.trueScope.children.isNotEmpty()) { val instruction = branchInstruction(stmt.condition, true) - val elseLabel = program.makeLabel("branch_else") + val elseLabel = makeLabel("branch_else") out(" $instruction $elseLabel") - translate(stmt.truepart) + translate(stmt.trueScope) out(elseLabel) } } else { val instruction = branchInstruction(stmt.condition, true) - val elseLabel = program.makeLabel("branch_else") - val endLabel = program.makeLabel("branch_end") + val elseLabel = makeLabel("branch_else") + val endLabel = makeLabel("branch_end") out(" $instruction $elseLabel") - translate(stmt.truepart) + translate(stmt.trueScope) jmp(endLabel) out(elseLabel) - translate(stmt.elsepart) + translate(stmt.falseScope) out(endLabel) } } } - private fun translate(stmt: Directive) { - when(stmt.directive) { - "%asminclude" -> { - val includedName = stmt.args[0].str!! - if(stmt.definingModule.source is SourceCode.Generated) - throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported") - loadAsmIncludeFile(includedName, stmt.definingModule.source).fold( - success = { assemblyLines.add(it.trimEnd().trimStart('\r', '\n')) }, - failure = { errors.err(it.toString(), stmt.position) } - ) - } - "%asmbinary" -> { - val includedName = stmt.args[0].str!! - val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else "" - val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else "" - if(stmt.definingModule.source is SourceCode.Generated) - throw AssemblyError("%asmbinary inside non-library/non-filesystem module not yet supported") - val sourcePath = Path(stmt.definingModule.source.origin) - val includedPath = sourcePath.resolveSibling(includedName) - val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file* - .toAbsolutePath() - .relativize(includedPath.toAbsolutePath()) - .normalize() // avoid assembler warnings (-Wportable; only some, not all) - .toString().replace('\\', '/') - out(" .binary \"$pathForAssembler\" $offset $length") - } - "%breakpoint" -> { - val label = "_prog8_breakpoint_${breakpointLabels.size+1}" - breakpointLabels.add(label) - out(label) - } - } - } - - private fun getJumpTarget(jump: Jump): Pair { + private fun getJumpTarget(jump: PtJump): Pair { val ident = jump.identifier val label = jump.generatedLabel val addr = jump.address return when { ident!=null -> { // can be a label, or a pointer variable - val target = ident.targetVarDecl(program) - if(target!=null) - Pair(asmSymbolName(ident), true) // indirect + val symbol = symbolTable.lookup(ident.name) + if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT)) + Pair(asmSymbolName(ident), true) // indirect jump if the jump symbol is a variable else Pair(asmSymbolName(ident), false) } @@ -886,21 +865,19 @@ $repeatLabel lda $counterVar } } - private fun translate(ret: Return, withRts: Boolean=true) { + private fun translate(ret: PtReturn, withRts: Boolean=true) { ret.value?.let { returnvalue -> - val sub = ret.definingSubroutine!! - val returnType = sub.returntypes.single() - val returnReg = sub.asmReturnvaluesRegisters.single() - if(returnReg.registerOrPair==null) - throw AssemblyError("normal subroutines can't return value in status register directly") - - when (returnType) { + val sub = ret.definingSub()!! + val returnReg = sub.returnRegister()!! + when (sub.returntype) { in NumericDatatypes -> { assignExpressionToRegister(returnvalue, returnReg.registerOrPair!!) } else -> { // all else take its address and assign that also to AY register pair - val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position) + val addrofValue = PtAddressOf(returnvalue.position) + addrofValue.add(returnvalue as PtIdentifier) + addrofValue.parent = ret.parent assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!, false) } } @@ -910,31 +887,29 @@ $repeatLabel lda $counterVar out(" rts") } - private fun translate(asm: InlineAssembly) { + private fun translate(asm: PtInlineAssembly) { assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n')) } - internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair { - return when (val targetRoutine = it.targetStatement(program)!!) { - is BuiltinFunctionPlaceholder -> { - val func = BuiltinFunctions.getValue(targetRoutine.name) - when (func.returnType) { - in ByteDatatypes -> RegisterOrPair.A - in WordDatatypes -> RegisterOrPair.AY - DataType.FLOAT -> RegisterOrPair.FAC1 - else -> { - when(builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }) { - in ByteDatatypes -> RegisterOrPair.A - in WordDatatypes -> RegisterOrPair.AY - DataType.FLOAT -> RegisterOrPair.FAC1 - else -> throw AssemblyError("weird returntype") - } - } - } - } - is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!! - else -> throw AssemblyError("invalid call target") - } + private fun translate(incbin: PtIncludeBinary) { + val offset = if(incbin.offset!=null) ", ${incbin.offset}" else "" + val length = if(incbin.length!=null) ", ${incbin.length}" else "" + if(incbin.definingBlock()!!.source is SourceCode.Generated) + throw AssemblyError("%asmbinary inside non-library/non-filesystem module not yet supported") + val sourcePath = Path(incbin.definingBlock()!!.source.origin) + val includedPath = sourcePath.resolveSibling(incbin.file) + val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file* + .toAbsolutePath() + .relativize(includedPath.toAbsolutePath()) + .normalize() // avoid assembler warnings (-Wportable; only some, not all) + .toString().replace('\\', '/') + out(" .binary \"$pathForAssembler\" $offset $length") + } + + private fun translate(brk: PtBreakpoint) { + val label = "_prog8_breakpoint_${breakpointLabels.size+1}" + breakpointLabels.add(label) + out(label) } internal fun signExtendAYlsb(valueDt: DataType) { @@ -987,8 +962,7 @@ $repeatLabel lda $counterVar } } - internal fun isZpVar(variable: IdentifierReference): Boolean = - allocator.isZpVar(variable.targetVarDecl(program)!!.scopedName) + internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name.split('.')) // TODO dotted string internal fun jmp(asmLabel: String, indirect: Boolean=false) { if(indirect) { @@ -1001,45 +975,45 @@ $repeatLabel lda $counterVar } } - internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair? { - if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") { - val leftDt = pointerOffsetExpr.left.inferType(program) - val rightDt = pointerOffsetExpr.left.inferType(program) - if(leftDt istype DataType.UWORD && rightDt istype DataType.UBYTE) + internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair? { + if(pointerOffsetExpr is PtBinaryExpression && pointerOffsetExpr.operator=="+") { + val leftDt = pointerOffsetExpr.left.type + val rightDt = pointerOffsetExpr.left.type + if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE) return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right) - if(leftDt istype DataType.UBYTE && rightDt istype DataType.UWORD) + if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD) return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left) - if(leftDt istype DataType.UWORD && rightDt istype DataType.UWORD) { + if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) { // could be that the index was a constant numeric byte but converted to word, check that - val constIdx = pointerOffsetExpr.right.constValue(program) + val constIdx = pointerOffsetExpr.right as? PtNumber if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) { - return Pair(pointerOffsetExpr.left, NumericLiteral(DataType.UBYTE, constIdx.number, constIdx.position)) + return Pair(pointerOffsetExpr.left, PtNumber(DataType.UBYTE, constIdx.number, constIdx.position)) } // could be that the index was typecasted into uword, check that - val rightTc = pointerOffsetExpr.right as? TypecastExpression - if(rightTc!=null && rightTc.expression.inferType(program) istype DataType.UBYTE) - return Pair(pointerOffsetExpr.left, rightTc.expression) - val leftTc = pointerOffsetExpr.left as? TypecastExpression - if(leftTc!=null && leftTc.expression.inferType(program) istype DataType.UBYTE) - return Pair(pointerOffsetExpr.right, leftTc.expression) + val rightTc = pointerOffsetExpr.right as? PtTypeCast + if(rightTc!=null && rightTc.value.type == DataType.UBYTE) + return Pair(pointerOffsetExpr.left, rightTc.value) + val leftTc = pointerOffsetExpr.left as? PtTypeCast + if(leftTc!=null && leftTc.value.type == DataType.UBYTE) + return Pair(pointerOffsetExpr.right, leftTc.value) } } return null } - internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean { + internal fun tryOptimizedPointerAccessWithA(expr: PtBinaryExpression, write: Boolean): Boolean { // optimize pointer,indexregister if possible - fun evalBytevalueWillClobberA(expr: Expression): Boolean { - val dt = expr.inferType(program) - if(dt isnot DataType.UBYTE && dt isnot DataType.BYTE) + fun evalBytevalueWillClobberA(expr: PtExpression): Boolean { + val dt = expr.type + if(dt != DataType.UBYTE && dt != DataType.BYTE) return true return when(expr) { - is IdentifierReference -> false - is NumericLiteral -> false - is DirectMemoryRead -> expr.addressExpression !is IdentifierReference && expr.addressExpression !is NumericLiteral - is TypecastExpression -> evalBytevalueWillClobberA(expr.expression) + is PtIdentifier -> false + is PtNumber -> false + is PtMemoryByte -> expr.address !is PtIdentifier && expr.address !is PtNumber + is PtTypeCast -> evalBytevalueWillClobberA(expr.value) else -> true } } @@ -1048,14 +1022,15 @@ $repeatLabel lda $counterVar if(expr.operator=="+") { val ptrAndIndex = pointerViaIndexRegisterPossible(expr) if(ptrAndIndex!=null) { - val pointervar = ptrAndIndex.first as? IdentifierReference - when(pointervar?.targetStatement(program)) { - is Label -> { + val pointervar = ptrAndIndex.first as? PtIdentifier + val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode + when(target) { + is PtLabel -> { assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - out(" lda ${asmSymbolName(pointervar)},y") + out(" lda ${asmSymbolName(pointervar!!)},y") return true } - is VarDecl, null -> { + is IPtVariable, null -> { if(write) { if(pointervar!=null && isZpVar(pointervar)) { val saveA = evalBytevalueWillClobberA(ptrAndIndex.second) @@ -1070,7 +1045,7 @@ $repeatLabel lda $counterVar val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) if(saveA) out(" pha") - if(ptrAndIndex.second.isSimple) { + if(ptrAndIndex.second.isSimple()) { assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) if(saveA) @@ -1091,7 +1066,7 @@ $repeatLabel lda $counterVar out(" lda (${asmSymbolName(pointervar)}),y") } else { // copy the pointer var to zp first - if(ptrAndIndex.second.isSimple) { + if(ptrAndIndex.second.isSimple()) { assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) out(" lda (P8ZP_SCRATCH_W2),y") @@ -1105,14 +1080,21 @@ $repeatLabel lda $counterVar } return true } - else -> throw AssemblyError("invalid pointervar") + else -> throw AssemblyError("invalid pointervar $pointervar") } } } return false } - private fun translateCompareAndJumpIfTrue(expr: BinaryExpression, jump: Jump) { + internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? { + val node = asmgen.symbolTable.lookup(name)!!.astNode + if(node is PtSubroutineParameter) + return node + return node.definingSub()?.parameters?.singleOrNull { it.name===name } + } + + private fun translateCompareAndJumpIfTrue(expr: PtBinaryExpression, jump: PtJump) { if(expr.operator !in ComparisonOperators) throw AssemblyError("must be comparison expression") @@ -1122,7 +1104,7 @@ $repeatLabel lda $counterVar val left = expr.left val right = expr.right - val rightConstVal = right.constValue(program) + val rightConstVal = right as? PtNumber val label = when { jump.generatedLabel!=null -> jump.generatedLabel!! @@ -1133,17 +1115,17 @@ $repeatLabel lda $counterVar if (rightConstVal!=null && rightConstVal.number == 0.0) testZeroAndJump(left, invertedComparisonOperator, label) else { - val leftConstVal = left.constValue(program) + val leftConstVal = left as? PtNumber testNonzeroComparisonAndJump(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal) } } - private fun translateCompareAndJumpIfFalse(expr: BinaryExpression, jumpIfFalseLabel: String) { + private fun translateCompareAndJumpIfFalse(expr: PtBinaryExpression, jumpIfFalseLabel: String) { val left = expr.left val right = expr.right val operator = expr.operator - val leftConstVal = left.constValue(program) - val rightConstVal = right.constValue(program) + val leftConstVal = left as? PtNumber + val rightConstVal = right as? PtNumber if (rightConstVal!=null && rightConstVal.number == 0.0) testZeroAndJump(left, operator, jumpIfFalseLabel) @@ -1152,12 +1134,12 @@ $repeatLabel lda $counterVar } private fun testZeroAndJump( - left: Expression, + left: PtExpression, operator: String, jumpIfFalseLabel: String ) { - val dt = left.inferType(program).getOr(DataType.UNDEFINED) - if(dt in IntegerDatatypes && left is IdentifierReference) + val dt = left.type + if(dt in IntegerDatatypes && left is PtIdentifier) return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel) when(dt) { @@ -1170,7 +1152,7 @@ $repeatLabel lda $counterVar } if(dt==DataType.UBYTE || dt==DataType.BOOL) { assignExpressionToRegister(left, RegisterOrPair.A, false) - if (left is IFunctionCall && !left.isSimple) + if (left is PtFunctionCall && !left.isSimple()) out(" cmp #0") } else { assignExpressionToRegister(left, RegisterOrPair.AY, false) @@ -1186,7 +1168,7 @@ $repeatLabel lda $counterVar } DataType.BYTE -> { assignExpressionToRegister(left, RegisterOrPair.A, true) - if (left is IFunctionCall && !left.isSimple) + if (left is PtFunctionCall && !left.isSimple()) out(" cmp #0") when (operator) { "==" -> out(" bne $jumpIfFalseLabel") @@ -1244,7 +1226,7 @@ $repeatLabel lda $counterVar } } - private fun testVariableZeroAndJump(variable: IdentifierReference, dt: DataType, operator: String, jumpIfFalseLabel: String) { + private fun testVariableZeroAndJump(variable: PtIdentifier, dt: DataType, operator: String, jumpIfFalseLabel: String) { // optimized code if the expression is just an identifier (variable) val varname = asmVariableName(variable) when(dt) { @@ -1305,14 +1287,14 @@ $repeatLabel lda $counterVar } private fun testNonzeroComparisonAndJump( - left: Expression, + left: PtExpression, operator: String, - right: Expression, + right: PtExpression, jumpIfFalseLabel: String, - leftConstVal: NumericLiteral?, - rightConstVal: NumericLiteral? + leftConstVal: PtNumber?, + rightConstVal: PtNumber? ) { - val dt = left.inferType(program).getOrElse { throw AssemblyError("unknown dt") } + val dt = left.type when (operator) { "==" -> { @@ -1320,7 +1302,7 @@ $repeatLabel lda $counterVar in ByteDatatypes -> translateByteEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) in WordDatatypes -> translateWordEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringEqualsJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringEqualsJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } @@ -1329,7 +1311,7 @@ $repeatLabel lda $counterVar in ByteDatatypes -> translateByteNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) in WordDatatypes -> translateWordNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringNotEqualsJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringNotEqualsJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } @@ -1340,7 +1322,7 @@ $repeatLabel lda $counterVar DataType.UWORD -> translateUwordLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.WORD -> translateWordLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringLessJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringLessJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } @@ -1351,7 +1333,7 @@ $repeatLabel lda $counterVar DataType.UWORD -> translateUwordLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.WORD -> translateWordLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringLessOrEqualJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringLessOrEqualJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } @@ -1362,7 +1344,7 @@ $repeatLabel lda $counterVar DataType.UWORD -> translateUwordGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.WORD -> translateWordGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatGreaterJump(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringGreaterJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringGreaterJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } @@ -1373,21 +1355,21 @@ $repeatLabel lda $counterVar DataType.UWORD -> translateUwordGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.WORD -> translateWordGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) DataType.FLOAT -> translateFloatGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringGreaterOrEqualJump(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel) + DataType.STR -> translateStringGreaterOrEqualJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } } } - private fun translateFloatLessJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -1400,7 +1382,7 @@ $repeatLabel lda $counterVar jsr floats.vars_less_f beq $jumpIfFalseLabel""") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -1413,7 +1395,7 @@ $repeatLabel lda $counterVar jsr floats.vars_less_f beq $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -1425,14 +1407,14 @@ $repeatLabel lda $counterVar } } - private fun translateFloatLessOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -1445,7 +1427,7 @@ $repeatLabel lda $counterVar jsr floats.vars_lesseq_f beq $jumpIfFalseLabel""") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -1458,7 +1440,7 @@ $repeatLabel lda $counterVar jsr floats.vars_lesseq_f beq $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -1470,11 +1452,11 @@ $repeatLabel lda $counterVar } } - private fun translateFloatGreaterJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -1487,10 +1469,10 @@ $repeatLabel lda $counterVar jsr floats.vars_less_f beq $jumpIfFalseLabel""") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -1503,7 +1485,7 @@ $repeatLabel lda $counterVar jsr floats.vars_less_f beq $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -1515,11 +1497,11 @@ $repeatLabel lda $counterVar } } - private fun translateFloatGreaterOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -1532,10 +1514,10 @@ $repeatLabel lda $counterVar jsr floats.vars_lesseq_f beq $jumpIfFalseLabel""") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -1548,7 +1530,7 @@ $repeatLabel lda $counterVar jsr floats.vars_lesseq_f beq $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -1560,7 +1542,7 @@ $repeatLabel lda $counterVar } } - private fun translateUbyteLessJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUbyteLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bcs $jumpIfFalseLabel") @@ -1572,7 +1554,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.A) code("#${rightConstVal.number.toInt()}") @@ -1580,7 +1562,7 @@ $repeatLabel lda $counterVar else jmp(jumpIfFalseLabel) } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { return if(rightConstVal.number.toInt()!=0) { translateDirectMemReadExpressionToRegAorStack(left, false) code("#${rightConstVal.number.toInt()}") @@ -1594,7 +1576,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -1605,7 +1587,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteLessJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" @@ -1622,7 +1604,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number}") @@ -1635,7 +1617,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -1646,7 +1628,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordLessJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUwordLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1664,7 +1646,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -1678,7 +1660,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) } else { @@ -1690,7 +1672,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel") } - private fun translateWordLessJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1708,7 +1690,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -1724,7 +1706,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) } else { @@ -1736,7 +1718,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel") } - private fun translateUbyteGreaterJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUbyteGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(""" @@ -1751,14 +1733,14 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") else out(" beq $jumpIfFalseLabel") } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { translateDirectMemReadExpressionToRegAorStack(left, false) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") @@ -1771,7 +1753,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -1782,7 +1764,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteGreaterJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" @@ -1801,7 +1783,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number}") @@ -1814,7 +1796,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -1825,7 +1807,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordGreaterJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUwordGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1844,7 +1826,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -1863,7 +1845,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) } else { @@ -1875,7 +1857,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2") } - private fun translateWordGreaterJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCmpOperand: String, lsbCmpOperand: String) { out(""" @@ -1893,7 +1875,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(right, RegisterOrPair.AY) val varname = asmVariableName(left) @@ -1914,7 +1896,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleLeftOperand(left, right, ::code)) return - if(right.isSimple) { + if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null) assignExpressionToRegister(right, RegisterOrPair.AY) } else { @@ -1926,7 +1908,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel") } - private fun translateUbyteLessOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUbyteLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(""" @@ -1942,14 +1924,14 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") else out(" bne $jumpIfFalseLabel") } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { translateDirectMemReadExpressionToRegAorStack(left, false) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") @@ -1962,7 +1944,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -1973,7 +1955,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteLessOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" clc @@ -1989,7 +1971,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number}") @@ -2005,7 +1987,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -2016,7 +1998,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordLessOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUwordLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2037,7 +2019,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -2056,7 +2038,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) } else { @@ -2068,7 +2050,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel") } - private fun translateWordLessOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(leftName: String) { out(""" @@ -2087,7 +2069,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(right, RegisterOrPair.AY) code(asmVariableName(left)) @@ -2106,12 +2088,12 @@ $repeatLabel lda $counterVar } } - if(left is IdentifierReference) { + if(left is PtIdentifier) { assignExpressionToRegister(right, RegisterOrPair.AY) return code(asmVariableName(left)) } - if(right.isSimple) { + if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null) assignExpressionToRegister(right, RegisterOrPair.AY) } else { @@ -2123,7 +2105,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel") } - private fun translateUbyteGreaterOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUbyteGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bcc $jumpIfFalseLabel") @@ -2135,14 +2117,14 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.A) code("#${rightConstVal.number.toInt()}") } return } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { if(rightConstVal.number.toInt()!=0) { translateDirectMemReadExpressionToRegAorStack(left, false) code("#${rightConstVal.number.toInt()}") @@ -2155,7 +2137,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -2166,7 +2148,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteGreaterOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" sec @@ -2182,7 +2164,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number}") @@ -2195,7 +2177,7 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) } else { @@ -2206,7 +2188,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordGreaterOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateUwordGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2224,7 +2206,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -2237,7 +2219,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(right.isSimple) { + if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(right, RegisterOrPair.AY) } else { @@ -2249,7 +2231,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel") } - private fun translateWordGreaterOrEqualJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2267,7 +2249,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { return if(rightConstVal.number.toInt()!=0) { assignExpressionToRegister(left, RegisterOrPair.AY) code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") @@ -2283,7 +2265,7 @@ $repeatLabel lda $counterVar if(wordJumpForSimpleRightOperands(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) } else { @@ -2295,7 +2277,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel") } - private fun translateByteEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bne $jumpIfFalseLabel") } @@ -2306,14 +2288,14 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") else out(" bne $jumpIfFalseLabel") } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { translateDirectMemReadExpressionToRegAorStack(left, false) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") @@ -2326,10 +2308,10 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) - } else if(right.isSimple) { + } else if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(right, RegisterOrPair.A) } else { @@ -2340,7 +2322,7 @@ $repeatLabel lda $counterVar out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel") } - private fun translateByteNotEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateByteNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | beq $jumpIfFalseLabel") @@ -2352,14 +2334,14 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") else out(" beq $jumpIfFalseLabel") } - else if (left is DirectMemoryRead) { + else if (left is PtMemoryByte) { translateDirectMemReadExpressionToRegAorStack(left, false) return if(rightConstVal.number.toInt()!=0) code("#${rightConstVal.number.toInt()}") @@ -2372,10 +2354,10 @@ $repeatLabel lda $counterVar if(byteJumpForSimpleRightOperand(left, right, ::code)) return - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(left, RegisterOrPair.A) - } else if(right.isSimple) { + } else if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) assignExpressionToRegister(right, RegisterOrPair.A) } else { @@ -2386,14 +2368,14 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateWordEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal!=leftConstVal) jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { val name = asmVariableName(left) if(rightConstVal.number!=0.0) { val rightNum = rightConstVal.number.toHex() @@ -2418,7 +2400,7 @@ $repeatLabel lda $counterVar } when (right) { - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(left, RegisterOrPair.AY) val number = right.number.toHex() out(""" @@ -2428,7 +2410,7 @@ $repeatLabel lda $counterVar bne $jumpIfFalseLabel """) } - is IdentifierReference -> { + is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.AY) out(""" cmp ${asmVariableName(right)} @@ -2437,7 +2419,7 @@ $repeatLabel lda $counterVar bne $jumpIfFalseLabel """) } - is AddressOf -> { + is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) out(""" @@ -2448,10 +2430,10 @@ $repeatLabel lda $counterVar """) } else -> { - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) - } else if(right.isSimple) { + } else if(right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(right, RegisterOrPair.AY) } else { @@ -2470,7 +2452,7 @@ $repeatLabel lda $counterVar } - private fun translateWordNotEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateWordNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { @@ -2478,7 +2460,7 @@ $repeatLabel lda $counterVar jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { val name = asmVariableName(left) if(rightConstVal.number.toInt()!=0) { val number = rightConstVal.number.toHex() @@ -2504,7 +2486,7 @@ $repeatLabel lda $counterVar } when (right) { - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(left, RegisterOrPair.AY) val number = right.number.toHex() out(""" @@ -2514,7 +2496,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel +""") } - is IdentifierReference -> { + is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.AY) out(""" cmp ${asmVariableName(right)} @@ -2523,7 +2505,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel +""") } - is AddressOf -> { + is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) out(""" @@ -2534,10 +2516,10 @@ $repeatLabel lda $counterVar +""") } else -> { - if(left.isSimple) { + if(left.isSimple()) { assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(left, RegisterOrPair.AY) - } else if (right.isSimple) { + } else if (right.isSimple()) { assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(right, RegisterOrPair.AY) } else { @@ -2558,14 +2540,14 @@ $repeatLabel lda $counterVar } - private fun translateFloatEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal!=leftConstVal) jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { val name = asmVariableName(left) when(rightConstVal.number) { @@ -2601,10 +2583,10 @@ $repeatLabel lda $counterVar if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -2617,7 +2599,7 @@ $repeatLabel lda $counterVar jsr floats.vars_equal_f beq $jumpIfFalseLabel""") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -2630,7 +2612,7 @@ $repeatLabel lda $counterVar jsr floats.vars_equal_f beq $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -2642,14 +2624,14 @@ $repeatLabel lda $counterVar } } - private fun translateFloatNotEqualsJump(left: Expression, right: Expression, leftConstVal: NumericLiteral?, rightConstVal: NumericLiteral?, jumpIfFalseLabel: String) { + private fun translateFloatNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal==leftConstVal) jmp(jumpIfFalseLabel) return } else { - if (left is IdentifierReference) { + if (left is PtIdentifier) { val name = asmVariableName(left) when(rightConstVal.number) { @@ -2686,10 +2668,10 @@ $repeatLabel lda $counterVar if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } - else if(leftConstVal!=null && right is IdentifierReference) { + else if(leftConstVal!=null && right is PtIdentifier) { throw AssemblyError("const-compare should have been optimized to have const as right operand") } - else if(left is IdentifierReference && rightConstVal!=null) { + else if(left is PtIdentifier && rightConstVal!=null) { val leftName = asmVariableName(left) val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" @@ -2702,7 +2684,7 @@ $repeatLabel lda $counterVar jsr floats.vars_equal_f bne $jumpIfFalseLabel""") } - else if(left is IdentifierReference && right is IdentifierReference) { + else if(left is PtIdentifier && right is PtIdentifier) { val leftName = asmVariableName(left) val rightName = asmVariableName(right) out(""" @@ -2715,7 +2697,7 @@ $repeatLabel lda $counterVar jsr floats.vars_equal_f bne $jumpIfFalseLabel""") } else { - val subroutine = left.definingSubroutine!! + val subroutine = left.definingSub()!! subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) @@ -2727,7 +2709,7 @@ $repeatLabel lda $counterVar } } - private fun translateStringEqualsJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringEqualsJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2742,7 +2724,7 @@ $repeatLabel lda $counterVar bne $jumpIfFalseLabel""") } - private fun translateStringNotEqualsJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringNotEqualsJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2757,7 +2739,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } - private fun translateStringLessJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringLessJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2771,7 +2753,7 @@ $repeatLabel lda $counterVar bpl $jumpIfFalseLabel""") } - private fun translateStringGreaterJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringGreaterJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2786,7 +2768,7 @@ $repeatLabel lda $counterVar bmi $jumpIfFalseLabel""") } - private fun translateStringLessOrEqualJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringLessOrEqualJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2802,7 +2784,7 @@ $repeatLabel lda $counterVar +""") } - private fun translateStringGreaterOrEqualJump(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) { + private fun translateStringGreaterOrEqualJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2818,10 +2800,10 @@ $repeatLabel lda $counterVar +""") } - internal fun translateDirectMemReadExpressionToRegAorStack(expr: DirectMemoryRead, pushResultOnEstack: Boolean) { + internal fun translateDirectMemReadExpressionToRegAorStack(expr: PtMemoryByte, pushResultOnEstack: Boolean) { fun assignViaExprEval() { - assignExpressionToVariable(expr.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) + assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD, null) if (isTargetCpu(CpuType.CPU65c02)) { if (pushResultOnEstack) { out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x") @@ -2837,21 +2819,21 @@ $repeatLabel lda $counterVar } } - when(expr.addressExpression) { - is NumericLiteral -> { - val address = (expr.addressExpression as NumericLiteral).number.toInt() + when(expr.address) { + is PtNumber -> { + val address = (expr.address as PtNumber).number.toInt() out(" lda ${address.toHex()}") if(pushResultOnEstack) out(" sta P8ESTACK_LO,x | dex") } - is IdentifierReference -> { + is PtIdentifier -> { // the identifier is a pointer variable, so read the value from the address in it - loadByteFromPointerIntoA(expr.addressExpression as IdentifierReference) + loadByteFromPointerIntoA(expr.address as PtIdentifier) if(pushResultOnEstack) out(" sta P8ESTACK_LO,x | dex") } - is BinaryExpression -> { - if(tryOptimizedPointerAccessWithA(expr.addressExpression as BinaryExpression, false)) { + is PtBinaryExpression -> { + if(tryOptimizedPointerAccessWithA(expr.address as PtBinaryExpression, false)) { if(pushResultOnEstack) out(" sta P8ESTACK_LO,x | dex") } else { @@ -2862,21 +2844,21 @@ $repeatLabel lda $counterVar } } - private fun wordJumpForSimpleLeftOperand(left: Expression, right: Expression, code: (String, String)->Unit): Boolean { + private fun wordJumpForSimpleLeftOperand(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean { when (left) { - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(right, RegisterOrPair.AY) val number = left.number.toHex() code("#>$number", "#<$number") return true } - is AddressOf -> { + is PtAddressOf -> { assignExpressionToRegister(right, RegisterOrPair.AY) val name = asmSymbolName(left.identifier) code("#>$name", "#<$name") return true } - is IdentifierReference -> { + is PtIdentifier -> { assignExpressionToRegister(right, RegisterOrPair.AY) val varname = asmVariableName(left) code("$varname+1", varname) @@ -2886,22 +2868,22 @@ $repeatLabel lda $counterVar } } - private fun byteJumpForSimpleRightOperand(left: Expression, right: Expression, code: (String)->Unit): Boolean { - if(right is NumericLiteral) { + private fun byteJumpForSimpleRightOperand(left: PtExpression, right: PtExpression, code: (String)->Unit): Boolean { + if(right is PtNumber) { assignExpressionToRegister(left, RegisterOrPair.A) code("#${right.number.toHex()}") return true } - if(right is IdentifierReference) { + if(right is PtIdentifier) { assignExpressionToRegister(left, RegisterOrPair.A) code(asmVariableName(right)) return true } - var memread = right as? DirectMemoryRead - if(memread==null && right is TypecastExpression) - memread = right.expression as? DirectMemoryRead + var memread = right as? PtMemoryByte + if(memread==null && right is PtTypeCast) + memread = right.value as? PtMemoryByte if(memread!=null) { - val address = memread.addressExpression as? NumericLiteral + val address = memread.address as? PtNumber if(address!=null) { assignExpressionToRegister(left, RegisterOrPair.A) code(address.number.toHex()) @@ -2911,21 +2893,21 @@ $repeatLabel lda $counterVar return false } - private fun wordJumpForSimpleRightOperands(left: Expression, right: Expression, code: (String, String)->Unit): Boolean { + private fun wordJumpForSimpleRightOperands(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean { when (right) { - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(left, RegisterOrPair.AY) val number = right.number.toHex() code("#>$number", "#<$number") return true } - is AddressOf -> { + is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) code("#>$name", "#<$name") return true } - is IdentifierReference -> { + is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.AY) val varname = asmVariableName(right) code("$varname+1", varname) @@ -2935,107 +2917,107 @@ $repeatLabel lda $counterVar } } - internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) { - // note: because A is pushed first so popped last, saving A is often not required here. - val parameter = target.subroutineParameter - if(parameter!=null) { - val sub = parameter.definingSubroutine!! - require(sub.isAsmSubroutine) { "push/pop arg passing only supported on asmsubs ${sub.position}" } - val shouldKeepA = sub.asmParameterRegisters.any { it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY } - val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)] - if(reg.statusflag!=null) { - if(shouldKeepA) - out(" sta P8ZP_SCRATCH_REG") - out(""" + internal fun popCpuStack(asmsub: PtAsmSub, parameter: PtSubroutineParameter, reg: RegisterOrStatusflag) { + val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY} + if(reg.statusflag!=null) { + if(shouldKeepA) + out(" sta P8ZP_SCRATCH_REG") + out(""" clc pla beq + sec +""") - if(shouldKeepA) - out(" lda P8ZP_SCRATCH_REG") - } - else { - if (dt in ByteDatatypes) { - if (isTargetCpu(CpuType.CPU65c02)) { - when (reg.registerOrPair) { - RegisterOrPair.A -> out(" pla") - RegisterOrPair.X -> out(" plx") - RegisterOrPair.Y -> out(" ply") - in Cx16VirtualRegisters -> out(" pla | sta cx16.${reg.registerOrPair!!.name.lowercase()}") - else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") - } - } else { - when (reg.registerOrPair) { - RegisterOrPair.A -> out(" pla") - RegisterOrPair.X -> { - if(shouldKeepA) - out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG") - else - out(" pla | tax") - } - RegisterOrPair.Y -> { - if(shouldKeepA) - out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG") - else - out(" pla | tay") - } - in Cx16VirtualRegisters -> out(" pla | sta cx16.${reg.registerOrPair!!.name.lowercase()}") - else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") - } + if(shouldKeepA) + out(" lda P8ZP_SCRATCH_REG") + } + else { + if (parameter.type in ByteDatatypes) { + if (isTargetCpu(CpuType.CPU65c02)) { + when (reg.registerOrPair) { + RegisterOrPair.A -> out(" pla") + RegisterOrPair.X -> out(" plx") + RegisterOrPair.Y -> out(" ply") + in Cx16VirtualRegisters -> out(" pla | sta cx16.${reg.registerOrPair!!.name.lowercase()}") + else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") } } else { - // word pop - if (isTargetCpu(CpuType.CPU65c02)) - when (reg.registerOrPair) { - RegisterOrPair.AX -> out(" plx | pla") - RegisterOrPair.AY -> out(" ply | pla") - RegisterOrPair.XY -> out(" ply | plx") - in Cx16VirtualRegisters -> { - val regname = reg.registerOrPair!!.name.lowercase() - out(" pla | sta cx16.$regname+1 | pla | sta cx16.$regname") - } - else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") + when (reg.registerOrPair) { + RegisterOrPair.A -> out(" pla") + RegisterOrPair.X -> { + if(shouldKeepA) + out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG") + else + out(" pla | tax") } - else { - when (reg.registerOrPair) { - RegisterOrPair.AX -> out(" pla | tax | pla") - RegisterOrPair.AY -> out(" pla | tay | pla") - RegisterOrPair.XY -> out(" pla | tay | pla | tax") - in Cx16VirtualRegisters -> { - val regname = reg.registerOrPair!!.name.lowercase() - out(" pla | sta cx16.$regname+1 | pla | sta cx16.$regname") - } - else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") + RegisterOrPair.Y -> { + if(shouldKeepA) + out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG") + else + out(" pla | tay") } + in Cx16VirtualRegisters -> out(" pla | sta cx16.${reg.registerOrPair!!.name.lowercase()}") + else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") + } + } + } else { + // word pop + if (isTargetCpu(CpuType.CPU65c02)) + when (reg.registerOrPair) { + RegisterOrPair.AX -> out(" plx | pla") + RegisterOrPair.AY -> out(" ply | pla") + RegisterOrPair.XY -> out(" ply | plx") + in Cx16VirtualRegisters -> { + val regname = reg.registerOrPair!!.name.lowercase() + out(" pla | sta cx16.$regname+1 | pla | sta cx16.$regname") + } + else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") + } + else { + when (reg.registerOrPair) { + RegisterOrPair.AX -> out(" pla | tax | pla") + RegisterOrPair.AY -> out(" pla | tay | pla") + RegisterOrPair.XY -> out(" pla | tay | pla | tax") + in Cx16VirtualRegisters -> { + val regname = reg.registerOrPair!!.name.lowercase() + out(" pla | sta cx16.$regname+1 | pla | sta cx16.$regname") + } + else -> throw AssemblyError("invalid target register ${reg.registerOrPair}") } } } + } + } + + internal fun popCpuStack(dt: DataType, target: IPtVariable, scope: IPtSubroutine?) { + // note: because A is pushed first so popped last, saving A is often not required here. + val targetAsmSub = (target as PtNode).definingAsmSub() + if(targetAsmSub != null) { + val parameter = targetAsmSub.parameters.first { it.second.name==target.name } + popCpuStack(targetAsmSub, parameter.second, parameter.first) + return + } + val scopedName = when(target) { + is PtConstant -> target.scopedName + is PtMemMapped -> target.scopedName + is PtVariable -> target.scopedName + else -> throw AssemblyError("weird target var") + } + val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.type, scope, variableAsmName = asmVariableName(scopedName)) + if (dt in ByteDatatypes) { + out(" pla") + assignRegister(RegisterOrPair.A, tgt) } else { - val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.datatype, scope, variableAsmName = asmVariableName(target.scopedName)) - if (dt in ByteDatatypes) { - out(" pla") - assignRegister(RegisterOrPair.A, tgt) - } else { - if (isTargetCpu(CpuType.CPU65c02)) - out(" ply | pla") - else - out(" pla | tay | pla") - assignRegister(RegisterOrPair.AY, tgt) - } + if (isTargetCpu(CpuType.CPU65c02)) + out(" ply | pla") + else + out(" pla | tay | pla") + assignRegister(RegisterOrPair.AY, tgt) } } - internal fun popCpuStack(dt: DataType, targetAsmVarName: String) { - when(dt) { - in ByteDatatypes -> out(" pla | sta $targetAsmVarName") - in WordDatatypes -> out(" pla | sta $targetAsmVarName+1 | pla | sta $targetAsmVarName") - else -> throw AssemblyError("can't pop $dt") - } - } - - internal fun pushCpuStack(dt: DataType, value: Expression) { - val signed = value.inferType(program).oneOf(DataType.BYTE, DataType.WORD) + internal fun pushCpuStack(dt: DataType, value: PtExpression) { + val signed = value.type.oneOf(DataType.BYTE, DataType.WORD) if(dt in ByteDatatypes) { assignExpressionToRegister(value, RegisterOrPair.A, signed) out(" pha") @@ -3050,12 +3032,12 @@ $repeatLabel lda $counterVar } } - internal fun needAsaveForExpr(arg: Expression): Boolean = - arg !is NumericLiteral && arg !is IdentifierReference && (arg !is DirectMemoryRead || !arg.isSimple) + internal fun needAsaveForExpr(arg: PtExpression): Boolean = + arg !is PtNumber && arg !is PtIdentifier && (arg !is PtMemoryByte || !arg.isSimple()) - private val subroutineExtrasCache = mutableMapOf() + private val subroutineExtrasCache = mutableMapOf() - internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo { + internal fun subroutineExtra(sub: IPtSubroutine): SubroutineExtraAsmInfo { var extra = subroutineExtrasCache[sub] return if(extra==null) { extra = SubroutineExtraAsmInfo() @@ -3065,8 +3047,15 @@ $repeatLabel lda $counterVar else extra } -} + private var generatedLabelSequenceNumber: Int = 0 + + internal fun makeLabel(postfix: String): String { + generatedLabelSequenceNumber++ + return "prog8_label_asm_${generatedLabelSequenceNumber}_$postfix" + } + +} /** * Contains various attributes that influence the assembly code generator. diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt index 41cd9a00e..871151ea3 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt @@ -1,16 +1,15 @@ package prog8.codegen.cpu6502 -import prog8.ast.Program -import prog8.ast.expressions.NumericLiteral -import prog8.ast.statements.VarDecl -import prog8.ast.statements.VarDeclType +import prog8.code.StConstant +import prog8.code.StMemVar +import prog8.code.SymbolTable import prog8.code.core.IMachineDefinition // note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations -internal fun optimizeAssembly(lines: MutableList, machine: IMachineDefinition, program: Program): Int { +internal fun optimizeAssembly(lines: MutableList, machine: IMachineDefinition, symbolTable: SymbolTable): Int { var numberOfOptimizations = 0 @@ -37,7 +36,7 @@ internal fun optimizeAssembly(lines: MutableList, machine: IMachineDefin numberOfOptimizations++ } - mods = optimizeStoreLoadSame(linesByFour, machine, program) + mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable) if(mods.isNotEmpty()) { apply(mods, lines) linesByFour = getLinesBy(lines, 4) @@ -52,14 +51,14 @@ internal fun optimizeAssembly(lines: MutableList, machine: IMachineDefin } var linesByFourteen = getLinesBy(lines, 14) - mods = optimizeSameAssignments(linesByFourteen, machine, program) + mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable) if(mods.isNotEmpty()) { apply(mods, lines) linesByFourteen = getLinesBy(lines, 14) numberOfOptimizations++ } - mods = optimizeSamePointerIndexing(linesByFourteen, machine, program) + mods = optimizeSamePointerIndexing(linesByFourteen) if(mods.isNotEmpty()) { apply(mods, lines) linesByFourteen = getLinesBy(lines, 14) @@ -129,7 +128,11 @@ private fun optimizeUselessStackByteWrites(linesByFour: List>>, machine: IMachineDefinition, program: Program): List { +private fun optimizeSameAssignments( + linesByFourteen: List>>, + machine: IMachineDefinition, + symbolTable: SymbolTable +): List { // Optimize sequential assignments of the same value to various targets (bytes, words, floats) // the float one is the one that requires 2*7=14 lines of code to check... @@ -154,8 +157,8 @@ private fun optimizeSameAssignments(linesByFourteen: List remove second lda/ldy pair (fifth and sixth lines) - val address1 = getAddressArg(first, program) - val address2 = getAddressArg(second, program) + val address1 = getAddressArg(first, symbolTable) + val address2 = getAddressArg(second, symbolTable) if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) { mods.add(Modification(lines[4].index, true, null)) mods.add(Modification(lines[5].index, true, null)) @@ -168,7 +171,7 @@ private fun optimizeSameAssignments(linesByFourteen: List remove second lda (third line) - val address = getAddressArg(first, program) + val address = getAddressArg(first, symbolTable) if(address==null || !machine.isIOAddress(address)) mods.add(Modification(lines[2].index, true, null)) } @@ -251,7 +254,7 @@ private fun optimizeSameAssignments(linesByFourteen: List>>, machine: IMachineDefinition, program: Program): List { +private fun optimizeSamePointerIndexing(linesByFourteen: List>>): List { // Optimize same pointer indexing where for instance we load and store to the same ptr index in Y // if Y isn't modified in between we can omit the second LDY: @@ -369,7 +372,11 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List>>, machine: IMachineDefinition, program: Program): List { +private fun optimizeStoreLoadSame( + linesByFour: List>>, + machine: IMachineDefinition, + symbolTable: SymbolTable +): List { // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated val mods = mutableListOf() for (lines in linesByFour) { @@ -397,7 +404,7 @@ private fun optimizeStoreLoadSame(linesByFour: List>>, } else { // no branch instruction follows, we can remove the load instruction - val address = getAddressArg(lines[2].value, program) + val address = getAddressArg(lines[2].value, symbolTable) address==null || !machine.isIOAddress(address) } @@ -439,7 +446,8 @@ private fun optimizeStoreLoadSame(linesByFour: List>>, private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""") -private fun getAddressArg(line: String, program: Program): UInt? { +private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? { + // try to get the constant value address, could return null if it's a symbol instead val loadArg = line.trimStart().substring(3).trim() return when { loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16) @@ -450,15 +458,11 @@ private fun getAddressArg(line: String, program: Program): UInt? { val identMatch = identifierRegex.find(loadArg) if(identMatch!=null) { val identifier = identMatch.value - val decl = program.toplevelModule.lookup(identifier.split('.')) as? VarDecl - if(decl!=null) { - when(decl.type){ - VarDeclType.VAR -> null - VarDeclType.CONST, - VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt() - } + when (val symbol = symbolTable.flat[identifier]) { + is StConstant -> symbol.value.toUInt() + is StMemVar -> symbol.address + else -> null } - else null } else null } else -> loadArg.substring(1).toUIntOrNull() diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt index 8f5d0f54f..d6475cb7a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt @@ -1,15 +1,12 @@ package prog8.codegen.cpu6502 -import prog8.ast.expressions.ArrayIndexedExpression -import prog8.ast.expressions.BuiltinFunctionCall -import prog8.ast.expressions.Expression -import prog8.ast.statements.Subroutine +import prog8.code.ast.* import prog8.code.core.Cx16VirtualRegisters import prog8.code.core.RegisterOrPair import prog8.code.core.RegisterOrStatusflag -fun asmsub6502ArgsEvalOrder(sub: Subroutine): List { +fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List { val order = mutableListOf() // order is: // 1) cx16 virtual word registers, @@ -17,43 +14,45 @@ fun asmsub6502ArgsEvalOrder(sub: Subroutine): List { // 3) single CPU registers (X last), except A, // 4) CPU Carry status flag // 5) the A register itself last (so everything before it can use the accumulator without having to save its value) - val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex() - val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters } + val args = sub.parameters.withIndex() + val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters } val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) - val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters } - val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A } - val (regA, rest) = args4.partition { it.value.second.registerOrPair != null } + val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters } + val (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A } + val (regA, rest) = args4.partition { it.value.first.registerOrPair != null } cx16regs.forEach { order += it.index } pairedRegs.forEach { order += it.index } regsWithoutA.forEach { - if(it.value.second.registerOrPair != RegisterOrPair.X) + if(it.value.first.registerOrPair != RegisterOrPair.X) order += it.index } - regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index} + regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index} rest.forEach { order += it.index } regA.forEach { order += it.index } require(order.size==sub.parameters.size) return order } -fun asmsub6502ArgsHaveRegisterClobberRisk(args: List, - paramRegisters: List): Boolean { - fun isClobberRisk(expr: Expression): Boolean { +fun asmsub6502ArgsHaveRegisterClobberRisk( + args: List, + params: List> +): Boolean { + fun isClobberRisk(expr: PtExpression): Boolean { when (expr) { - is ArrayIndexedExpression -> { - return paramRegisters.any { - it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) + is PtArrayIndexer -> { + return params.any { + it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) } } - is BuiltinFunctionCall -> { + is PtBuiltinFunctionCall -> { if (expr.name == "lsb" || expr.name == "msb") return isClobberRisk(expr.args[0]) if (expr.name == "mkword") return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1]) - return !expr.isSimple + return !expr.isSimple() } - else -> return !expr.isSimple + else -> return !expr.isSimple() } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt index 5c48f045c..d1bc9936a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt @@ -3,7 +3,6 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Result import com.github.michaelbull.result.mapError -import prog8.ast.generatedLabelPrefix import prog8.code.core.* import java.io.File import java.nio.file.Path @@ -63,7 +62,7 @@ internal class AssemblyProgram( "atari" -> { // Atari800XL .xex generation. - // TODO are these options okay? + // TODO are these options okay for atari? val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror", "--no-monitor" @@ -104,7 +103,7 @@ internal class AssemblyProgram( } private fun removeGeneratedLabelsFromMonlist() { - val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""") + val pattern = Regex("""al (\w+) \S+prog8_label_.+?""") val lines = viceMonListFile.toFile().readLines() viceMonListFile.toFile().outputStream().bufferedWriter().use { for (line in lines) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 12ab41db1..1254981a2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1,49 +1,39 @@ package prog8.codegen.cpu6502 -import prog8.ast.IFunctionCall -import prog8.ast.Node -import prog8.ast.Program -import prog8.ast.expressions.* -import prog8.ast.statements.ArrayIndex -import prog8.ast.statements.BuiltinFunctionCallStatement -import prog8.ast.statements.Subroutine +import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.assignment.* -import prog8.compiler.BuiltinFunctions -import prog8.compiler.FSignature -internal class BuiltinFunctionsAsmGen(private val program: Program, - private val asmgen: AsmGen, +internal class BuiltinFunctionsAsmGen(private val program: PtProgram, + private val asmgen: AsmGen6502Internal, private val assignAsmGen: AssignmentAsmGen) { - internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { - val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single()) - translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) + internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? { + return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) } - internal fun translateFunctioncallStatement(fcall: BuiltinFunctionCallStatement) { - val func = BuiltinFunctions.getValue(fcall.name) - translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null) + internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) { + translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null) } - private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { - if (discardResult && func.pure) - return // can just ignore the whole function call altogether + private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? { + if (discardResult && fcall.hasNoSideEffects) + return null // can just ignore the whole function call altogether if(discardResult && resultToStack) throw AssemblyError("cannot both discard the result AND put it onto stack") - val sscope = (fcall as Node).definingSubroutine + val sscope = fcall.definingISub() - when (func.name) { + when (fcall.name) { "msb" -> funcMsb(fcall, resultToStack, resultRegister) "lsb" -> funcLsb(fcall, resultToStack, resultRegister) "mkword" -> funcMkword(fcall, resultToStack, resultRegister) - "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) - "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) - "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) - "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) + "abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope) + "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope) + "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope) + "sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope) "rol" -> funcRol(fcall) "rol2" -> funcRol2(fcall) "ror" -> funcRor(fcall) @@ -59,16 +49,20 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, "push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0]) "pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0]) "pop" -> { - require(fcall.args[0] is IdentifierReference) { - "attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}" + require(fcall.args[0] is PtIdentifier) { + "attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}" } - asmgen.popCpuStack(DataType.UBYTE, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine) + val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name) + val target = symbol!!.astNode as IPtVariable + asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub()) } "popw" -> { - require(fcall.args[0] is IdentifierReference) { - "attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}" + require(fcall.args[0] is PtIdentifier) { + "attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}" } - asmgen.popCpuStack(DataType.UWORD, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine) + val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name) + val target = symbol!!.astNode as IPtVariable + asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub()) } "rsave" -> funcRsave() "rsavex" -> funcRsaveX() @@ -77,8 +71,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, "cmp" -> funcCmp(fcall) "callfar" -> funcCallFar(fcall) "callrom" -> funcCallRom(fcall) - else -> throw AssemblyError("missing asmgen for builtin func ${func.name}") + else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") } + + return BuiltinFunctions.getValue(fcall.name).returnType } private fun funcRsave() { @@ -132,29 +128,29 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1") } - private fun funcCallFar(fcall: IFunctionCall) { + private fun funcCallFar(fcall: PtBuiltinFunctionCall) { if(asmgen.options.compTarget.name != "cx16") throw AssemblyError("callfar only works on cx16 target at this time") - val bank = fcall.args[0].constValue(program)?.number?.toInt() - val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0 + val bank = fcall.args[0].asConstInteger() + val address = fcall.args[1].asConstInteger() ?: 0 val argAddrArg = fcall.args[2] if(bank==null) throw AssemblyError("callfar (jsrfar) bank has to be a constant") - if(fcall.args[1].constValue(program) == null) { + if(fcall.args[1] !is PtNumber) { assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false) asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word") } - if(argAddrArg.constValue(program)?.number == 0.0) { + if(argAddrArg.asConstInteger() == 0) { asmgen.out(""" jsr cx16.jsrfar + .word ${address.toHex()} .byte ${bank.toHex()}""") } else { when(argAddrArg) { - is AddressOf -> { - if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE) + is PtAddressOf -> { + if(argAddrArg.identifier.type != DataType.UBYTE) throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE") asmgen.out(""" lda ${asmgen.asmVariableName(argAddrArg.identifier)} @@ -163,7 +159,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, .byte ${bank.toHex()} sta ${asmgen.asmVariableName(argAddrArg.identifier)}""") } - is NumericLiteral -> { + is PtNumber -> { asmgen.out(""" lda ${argAddrArg.number.toHex()} jsr cx16.jsrfar @@ -176,12 +172,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcCallRom(fcall: IFunctionCall) { + private fun funcCallRom(fcall: PtBuiltinFunctionCall) { if(asmgen.options.compTarget.name != "cx16") throw AssemblyError("callrom only works on cx16 target at this time") - val bank = fcall.args[0].constValue(program)?.number?.toInt() - val address = fcall.args[1].constValue(program)?.number?.toInt() + val bank = fcall.args[0].asConstInteger() + val address = fcall.args[1].asConstInteger() if(bank==null || address==null) throw AssemblyError("callrom requires constant arguments") @@ -191,7 +187,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, throw AssemblyError("callrom bank must be <32") val argAddrArg = fcall.args[2] - if(argAddrArg.constValue(program)?.number == 0.0) { + if(argAddrArg.asConstInteger() == 0) { asmgen.out(""" lda $01 pha @@ -202,8 +198,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, sta $01""") } else { when(argAddrArg) { - is AddressOf -> { - if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE) + is PtAddressOf -> { + if(argAddrArg.identifier.type != DataType.UBYTE) throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE") asmgen.out(""" lda $01 @@ -216,7 +212,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, pla sta $01""") } - is NumericLiteral -> { + is PtNumber -> { asmgen.out(""" lda $01 pha @@ -233,46 +229,44 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcCmp(fcall: IFunctionCall) { + private fun funcCmp(fcall: PtBuiltinFunctionCall) { val arg1 = fcall.args[0] val arg2 = fcall.args[1] - val dt1 = arg1.inferType(program).getOrElse { throw AssemblyError("unknown dt") } - val dt2 = arg2.inferType(program).getOrElse { throw AssemblyError("unknown dt") } - if(dt1 in ByteDatatypes) { - if(dt2 in ByteDatatypes) { + if(arg1.type in ByteDatatypes) { + if(arg2.type in ByteDatatypes) { when (arg2) { - is IdentifierReference -> { + is PtIdentifier -> { asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}") } - is NumericLiteral -> { + is PtNumber -> { asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.out(" cmp #${arg2.number.toInt()}") } - is DirectMemoryRead -> { - if(arg2.addressExpression is NumericLiteral) { + is PtMemoryByte -> { + if(arg2.address is PtNumber) { asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) - asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}") + asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}") } else { - if(arg1.isSimple) { - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) + if(arg1.isSimple()) { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub()) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.out(" cmp P8ZP_SCRATCH_B1") } else { asmgen.pushCpuStack(DataType.UBYTE, arg1) - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub()) asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") } } } else -> { - if(arg1.isSimple) { - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) + if(arg1.isSimple()) { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub()) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.out(" cmp P8ZP_SCRATCH_B1") } else { asmgen.pushCpuStack(DataType.UBYTE, arg1) - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub()) asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") } } @@ -280,10 +274,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } else throw AssemblyError("args for cmp() should have same dt") } else { - // dt1 is a word - if(dt2 in WordDatatypes) { + // arg1 is a word + if(arg2.type in WordDatatypes) { when (arg2) { - is IdentifierReference -> { + is PtIdentifier -> { asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.out(""" cpy ${asmgen.asmVariableName(arg2)}+1 @@ -291,7 +285,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, cmp ${asmgen.asmVariableName(arg2)} +""") } - is NumericLiteral -> { + is PtNumber -> { asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.out(""" cpy #>${arg2.number.toInt()} @@ -300,8 +294,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, +""") } else -> { - if(arg1.isSimple) { - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine) + if(arg1.isSimple()) { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub()) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.out(""" cpy P8ZP_SCRATCH_W1+1 @@ -310,7 +304,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, +""") } else { asmgen.pushCpuStack(DataType.UWORD, arg1) - asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine) + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub()) asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.out(""" @@ -326,14 +320,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { - if(discardResult || fcall !is BuiltinFunctionCall) + private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + if(discardResult) throw AssemblyError("should not discard result of memory allocation at $fcall") - val name = (fcall.args[0] as StringLiteral).value + val name = (fcall.args[0] as PtString).value require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"} - val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position) - slabname.linkParents(fcall) - val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position)) + val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position) + val addressOf = PtAddressOf(fcall.position) + addressOf.add(slabname) + addressOf.parent = fcall + val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf) val target = if(resultToStack) AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null) @@ -343,8 +339,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, asmgen.translateNormalAssignment(assign) } - private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - translateArguments(fcall.args, func, scope) + private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { + translateArguments(fcall, scope) if(resultToStack) asmgen.out(" jsr prog8_lib.func_sqrt16_stack") else { @@ -353,13 +349,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcReverse(fcall: IFunctionCall) { + private fun funcReverse(fcall: PtBuiltinFunctionCall) { val variable = fcall.args.single() - if (variable is IdentifierReference) { - val decl = variable.targetVarDecl(program)!! + if (variable is PtIdentifier) { + val symbol = asmgen.symbolTable.lookup(variable.name) + val decl = symbol!!.astNode as IPtVariable + val numElements = when(decl) { + is PtConstant -> throw AssemblyError("cannot reverse a constant") + is PtMemMapped -> decl.arraySize + is PtVariable -> decl.arraySize + } val varName = asmgen.asmVariableName(variable) - val numElements = decl.arraysize!!.constIndex() - when (decl.datatype) { + when (decl.type) { DataType.ARRAY_UB, DataType.ARRAY_B -> { asmgen.out(""" lda #<$varName @@ -392,13 +393,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcSort(fcall: IFunctionCall) { + private fun funcSort(fcall: PtBuiltinFunctionCall) { val variable = fcall.args.single() - if (variable is IdentifierReference) { - val decl = variable.targetVarDecl(program)!! + if (variable is PtIdentifier) { + val symbol = asmgen.symbolTable.lookup(variable.name) + val decl = symbol!!.astNode as IPtVariable val varName = asmgen.asmVariableName(variable) - val numElements = decl.arraysize!!.constIndex() - when (decl.datatype) { + val numElements = when(decl) { + is PtConstant -> throw AssemblyError("cannot sort a constant") + is PtMemMapped -> decl.arraySize + is PtVariable -> decl.arraySize + } + when (decl.type) { DataType.ARRAY_UB, DataType.ARRAY_B -> { asmgen.out(""" lda #<$varName @@ -406,7 +412,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1 lda #$numElements""") - asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b") + asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b") } DataType.ARRAY_UW, DataType.ARRAY_W -> { asmgen.out(""" @@ -415,7 +421,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1 lda #$numElements""") - asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w") + asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w") } DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported") else -> throw AssemblyError("weird type") @@ -424,26 +430,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, throw AssemblyError("weird type") } - private fun funcRor2(fcall: IFunctionCall) { + private fun funcRor2(fcall: PtBuiltinFunctionCall) { val what = fcall.args.single() - val dt = what.inferType(program) - when (dt.getOr(DataType.UNDEFINED)) { + when (what.type) { DataType.UBYTE -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'b') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "ror2", 'b') asmgen.out(" jsr prog8_lib.ror2_array_ub") } - is DirectMemoryRead -> { - if (what.addressExpression is NumericLiteral) { - val number = (what.addressExpression as NumericLiteral).number + is PtMemoryByte -> { + if (what.address is PtNumber) { + val number = (what.address as PtNumber).number asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}") } else { - asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) + asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.out(" jsr prog8_lib.ror2_mem_ub") } } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable") } @@ -452,11 +457,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } DataType.UWORD -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'w') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "ror2", 'w') asmgen.out(" jsr prog8_lib.ror2_array_uw") } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ") } @@ -467,26 +472,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcRor(fcall: IFunctionCall) { + private fun funcRor(fcall: PtBuiltinFunctionCall) { val what = fcall.args.single() - val dt = what.inferType(program) - when (dt.getOr(DataType.UNDEFINED)) { + when (what.type) { DataType.UBYTE -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'b') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "ror", 'b') asmgen.out(" jsr prog8_lib.ror_array_ub") } - is DirectMemoryRead -> { - if (what.addressExpression is NumericLiteral) { - val number = (what.addressExpression as NumericLiteral).number + is PtMemoryByte -> { + if (what.address is PtNumber) { + val number = (what.address as PtNumber).number asmgen.out(" ror ${number.toHex()}") } else { - val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression) + val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) if(ptrAndIndex!=null) { - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.out(""" @@ -495,7 +499,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, + ror ${'$'}ffff,x ; modified""") asmgen.restoreRegisterLocal(CpuRegister.X) } else { - asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) + asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.out(""" sta (+) + 1 sty (+) + 2 @@ -503,7 +507,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" ror $variable") } @@ -512,11 +516,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } DataType.UWORD -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'w') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "ror", 'w') asmgen.out(" jsr prog8_lib.ror_array_uw") } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" ror $variable+1 | ror $variable") } @@ -527,26 +531,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcRol2(fcall: IFunctionCall) { + private fun funcRol2(fcall: PtBuiltinFunctionCall) { val what = fcall.args.single() - val dt = what.inferType(program) - when (dt.getOr(DataType.UNDEFINED)) { + when (what.type) { DataType.UBYTE -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'b') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "rol2", 'b') asmgen.out(" jsr prog8_lib.rol2_array_ub") } - is DirectMemoryRead -> { - if (what.addressExpression is NumericLiteral) { - val number = (what.addressExpression as NumericLiteral).number + is PtMemoryByte -> { + if (what.address is PtNumber) { + val number = (what.address as PtNumber).number asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}") } else { - asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) + asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.out(" jsr prog8_lib.rol2_mem_ub") } } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable") } @@ -555,11 +558,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } DataType.UWORD -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'w') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "rol2", 'w') asmgen.out(" jsr prog8_lib.rol2_array_uw") } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ") } @@ -570,26 +573,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcRol(fcall: IFunctionCall) { + private fun funcRol(fcall: PtBuiltinFunctionCall) { val what = fcall.args.single() - val dt = what.inferType(program) - when (dt.getOr(DataType.UNDEFINED)) { + when (what.type) { DataType.UBYTE -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'b') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "rol", 'b') asmgen.out(" jsr prog8_lib.rol_array_ub") } - is DirectMemoryRead -> { - if (what.addressExpression is NumericLiteral) { - val number = (what.addressExpression as NumericLiteral).number + is PtMemoryByte -> { + if (what.address is PtNumber) { + val number = (what.address as PtNumber).number asmgen.out(" rol ${number.toHex()}") } else { - val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression) + val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) if(ptrAndIndex!=null) { - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.out(""" @@ -598,7 +600,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, + rol ${'$'}ffff,x ; modified""") asmgen.restoreRegisterLocal(CpuRegister.X) } else { - asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) + asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.out(""" sta (+) + 1 sty (+) + 2 @@ -606,7 +608,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" rol $variable") } @@ -615,11 +617,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } DataType.UWORD -> { when (what) { - is ArrayIndexedExpression -> { - translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'w') + is PtArrayIndexer -> { + translateRolRorArrayArgs(what.variable, what, "rol", 'w') asmgen.out(" jsr prog8_lib.rol_array_uw") } - is IdentifierReference -> { + is PtIdentifier -> { val variable = asmgen.asmVariableName(what) asmgen.out(" rol $variable | rol $variable+1") } @@ -630,22 +632,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) { - if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) { + private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) { + if(arrayvar.type==DataType.UWORD) { if(dt!='b') throw AssemblyError("non-array var indexing requires bytes dt") asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null) } else { - asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null) + val addressOf = PtAddressOf(arrayvar.position) + addressOf.add(arrayvar) + addressOf.parent = arrayvar.parent.parent + asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null) } - asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null) + asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null) } - private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - translateArguments(fcall.args, func, scope) - val dt = fcall.args.single().inferType(program) + private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { + translateArguments(fcall, scope) + val dt = fcall.args.single().type if(resultToStack) { - when (dt.getOr(DataType.UNDEFINED)) { + when (dt) { DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack") DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack") DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack") @@ -654,7 +659,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, else -> throw AssemblyError("weird type $dt") } } else { - when (dt.getOr(DataType.UNDEFINED)) { + when (dt) { DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A") DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A") DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A") @@ -666,30 +671,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { + private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { outputAddressAndLenghtOfArray(fcall.args[0]) - val dt = fcall.args.single().inferType(program) + val dt = fcall.args.single().type if(resultToStack) { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack") - DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack") + when (dt) { + DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack") + DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack") + DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack") else -> throw AssemblyError("weird type $dt") } } else { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0") - DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A | ldy #0") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A | ldy #0") + when (dt) { + DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0") + DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0") + DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0") else -> throw AssemblyError("weird type $dt") } assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A) } } - private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - translateArguments(fcall.args, func, scope) - val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED) + private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { + translateArguments(fcall, scope) + val dt = fcall.args.single().type if(resultToStack) { when (dt) { DataType.UBYTE -> asmgen.out(" ldy #0") @@ -710,19 +715,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcPokeW(fcall: IFunctionCall) { + private fun funcPokeW(fcall: PtBuiltinFunctionCall) { when(val addrExpr = fcall.args[0]) { - is NumericLiteral -> { + is PtNumber -> { asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) val addr = addrExpr.number.toHex() asmgen.out(" sta $addr | sty ${addr}+1") return } - is IdentifierReference -> { + is PtIdentifier -> { val varname = asmgen.asmVariableName(addrExpr) if(asmgen.isZpVar(addrExpr)) { // pointervar is already in the zero page, no need to copy - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) if (asmgen.isTargetCpu(CpuType.CPU65c02)) { asmgen.out(""" @@ -742,14 +747,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, return } } - is BinaryExpression -> { - if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) { - val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference) - if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) { + is PtBinaryExpression -> { + if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) { + val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier) + if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) { // pointervar is already in the zero page, no need to copy - asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) - val index = (addrExpr.right as NumericLiteral).number.toHex() + val index = (addrExpr.right as PtNumber).number.toHex() asmgen.out(""" ldy #$index sta ($varname),y @@ -769,13 +774,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, asmgen.out(" jsr prog8_lib.func_pokew") } - private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { when(val addrExpr = fcall.args[0]) { - is NumericLiteral -> { + is PtNumber -> { val addr = addrExpr.number.toHex() asmgen.out(" lda $addr | ldy ${addr}+1") } - is IdentifierReference -> { + is PtIdentifier -> { val varname = asmgen.asmVariableName(addrExpr) if(asmgen.isZpVar(addrExpr)) { // pointervar is already in the zero page, no need to copy @@ -800,12 +805,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, asmgen.out(" jsr prog8_lib.func_peekw") } } - is BinaryExpression -> { - if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) { - val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference) - if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) { + is PtBinaryExpression -> { + if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) { + val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier) + if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) { // pointervar is already in the zero page, no need to copy - val index = (addrExpr.right as NumericLiteral).number.toHex() + val index = (addrExpr.right as PtNumber).number.toHex() asmgen.out(""" ldy #$index lda ($varname),y @@ -845,7 +850,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { if(resultToStack) { asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb @@ -854,12 +859,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, val reg = resultRegister ?: RegisterOrPair.AY var needAsave = asmgen.needAsaveForExpr(fcall.args[0]) if(!needAsave) { - val mr0 = fcall.args[0] as? DirectMemoryRead - val mr1 = fcall.args[1] as? DirectMemoryRead + val mr0 = fcall.args[0] as? PtMemoryByte + val mr1 = fcall.args[1] as? PtMemoryByte if (mr0 != null) - needAsave = mr0.addressExpression !is NumericLiteral + needAsave = mr0.address !is PtNumber if (mr1 != null) - needAsave = needAsave or (mr1.addressExpression !is NumericLiteral) + needAsave = needAsave or (mr1.address !is PtNumber) } when(reg) { RegisterOrPair.AX -> { @@ -898,13 +903,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + private fun funcMsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { val arg = fcall.args.single() - if (!arg.inferType(program).isWords) + if (arg.type !in WordDatatypes) throw AssemblyError("msb required word argument") - if (arg is NumericLiteral) + if (arg is PtNumber) throw AssemblyError("msb(const) should have been const-folded away") - if (arg is IdentifierReference) { + if (arg is PtIdentifier) { val sourceName = asmgen.asmVariableName(arg) if(resultToStack) { asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex") @@ -952,14 +957,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + private fun funcLsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { val arg = fcall.args.single() - if (!arg.inferType(program).isWords) + if (arg.type !in WordDatatypes) throw AssemblyError("lsb required word argument") - if (arg is NumericLiteral) + if (arg is PtNumber) throw AssemblyError("lsb(const) should have been const-folded away") - if (arg is IdentifierReference) { + if (arg is PtIdentifier) { val sourceName = asmgen.asmVariableName(arg) if(resultToStack) { asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex") @@ -1013,35 +1018,39 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun outputAddressAndLenghtOfArray(arg: Expression) { + private fun outputAddressAndLenghtOfArray(arg: PtExpression) { // address in P8ZP_SCRATCH_W1, number of elements in A - arg as IdentifierReference - val arrayVar = arg.targetVarDecl(program)!! - if(!arrayVar.isArray) - throw AssemblyError("length of non-array requested") - val size = arrayVar.arraysize!!.constIndex()!! + arg as PtIdentifier + val symbol = asmgen.symbolTable.lookup(arg.name) + val arrayVar = symbol!!.astNode as IPtVariable + val numElements = when(arrayVar) { + is PtConstant -> null + is PtMemMapped -> arrayVar.arraySize + is PtVariable -> arrayVar.arraySize + } ?: throw AssemblyError("length of non-array requested") val identifierName = asmgen.asmVariableName(arg) asmgen.out(""" lda #<$identifierName ldy #>$identifierName sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1 - lda #$size + lda #$numElements """) } - private fun translateArguments(args: MutableList, signature: FSignature, scope: Subroutine?) { - val callConv = signature.callConvention(args.map { - it.inferType(program).getOrElse { throw AssemblyError("unknown dt") } - }) + private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) { + val signature = BuiltinFunctions.getValue(call.name) + val callConv = signature.callConvention(call.args.map { it.type}) - fun getSourceForFloat(value: Expression): AsmAssignSource { + fun getSourceForFloat(value: PtExpression): AsmAssignSource { return when (value) { - is IdentifierReference -> { - val addr = AddressOf(value, value.position) + is PtIdentifier -> { + val addr = PtAddressOf(value.position) + addr.add(value) + addr.parent = call AsmAssignSource.fromAstSource(addr, program, asmgen) } - is NumericLiteral -> { + is PtNumber -> { throw AssemblyError("float literals should have been converted into autovar") } else -> { @@ -1049,27 +1058,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, throw AssemblyError("cannot use float arguments outside of a subroutine scope") asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true - val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position) - val addr = AddressOf(variable, value.position) - addr.linkParents(value) + val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position) + val addr = PtAddressOf(value.position) + addr.add(variable) + addr.parent = call asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope) AsmAssignSource.fromAstSource(addr, program, asmgen) } } } - args.zip(callConv.params).zip(signature.parameters).forEach { + call.args.zip(callConv.params).zip(signature.parameters).forEach { val paramName = it.second.name val conv = it.first.second val value = it.first.first when { conv.variable -> { - val varname = "prog8_lib.func_${signature.name}._arg_${paramName}" + val varname = "prog8_lib.func_${call.name}._arg_${paramName}" val src = when (conv.dt) { DataType.FLOAT -> getSourceForFloat(value) in PassByReferenceDatatypes -> { // put the address of the argument in AY - val addr = AddressOf(value as IdentifierReference, value.position) + val addr = PtAddressOf(value.position) + addr.add(value) + addr.parent = call AsmAssignSource.fromAstSource(addr, program, asmgen) } else -> { @@ -1085,7 +1097,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, DataType.FLOAT -> getSourceForFloat(value) in PassByReferenceDatatypes -> { // put the address of the argument in AY - val addr = AddressOf(value as IdentifierReference, value.position) + val addr = PtAddressOf(value.position) + addr.add(value) + addr.parent = call AsmAssignSource.fromAstSource(addr, program, asmgen) } else -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index fae2a6fb6..da44ef6eb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -1,16 +1,15 @@ package prog8.codegen.cpu6502 -import prog8.ast.Program -import prog8.ast.expressions.* +import prog8.code.ast.* import prog8.code.core.* import kotlin.math.absoluteValue -internal class ExpressionsAsmGen(private val program: Program, - private val asmgen: AsmGen, +internal class ExpressionsAsmGen(private val program: PtProgram, + private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator) { @Deprecated("avoid calling this as it generates slow evalstack based code") - internal fun translateExpression(expression:Expression) { + internal fun translateExpression(expression: PtExpression) { if (this.asmgen.options.slowCodegenWarnings) { asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position) } @@ -21,41 +20,42 @@ internal class ExpressionsAsmGen(private val program: Program, // the rest of the methods are all PRIVATE - private fun translateExpressionInternal(expression: Expression) { + private fun translateExpressionInternal(expression: PtExpression) { when(expression) { - is PrefixExpression -> translateExpression(expression) - is BinaryExpression -> translateExpression(expression) - is ArrayIndexedExpression -> translateExpression(expression) - is TypecastExpression -> translateExpression(expression) - is AddressOf -> translateExpression(expression) - is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true) - is NumericLiteral -> translateExpression(expression) - is IdentifierReference -> translateExpression(expression) - is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression) - is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null) - is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported") - is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") - is RangeExpression -> throw AssemblyError("range expression should have been changed into array values") - is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding") + is PtPrefix -> translateExpression(expression) + is PtBinaryExpression -> translateExpression(expression) + is PtArrayIndexer -> translateExpression(expression) + is PtTypeCast -> translateExpression(expression) + is PtAddressOf -> translateExpression(expression) + is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true) + is PtNumber -> translateExpression(expression) + is PtIdentifier -> translateExpression(expression) + is PtFunctionCall -> translateFunctionCallResultOntoStack(expression) + is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null) + is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported") + is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") + is PtRange -> throw AssemblyError("range expression should have been changed into array values") + is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code") else -> TODO("missing expression asmgen for $expression") } } - private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) { + private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) { // only for use in nested expression evaluation - val sub = call.target.targetSubroutine(program)!! + val symbol = asmgen.symbolTable.lookup(call.name) + val sub = symbol!!.astNode as IPtSubroutine asmgen.saveXbeforeCall(call) - asmgen.translateFunctionCall(call, true) + asmgen.translateFunctionCall(call) if(sub.regXasResult()) { // store the return value in X somewhere that we can access again below asmgen.out(" stx P8ZP_SCRATCH_REG") } asmgen.restoreXafterCall(call) - val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) - for ((_, reg) in returns) { + val returns: List> = sub.returnsWhatWhere() + for ((reg, _) in returns) { // result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree) if (reg.registerOrPair != null) { when (reg.registerOrPair!!) { @@ -133,9 +133,9 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateExpression(typecast: TypecastExpression) { - translateExpressionInternal(typecast.expression) - when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) { + private fun translateExpression(typecast: PtTypeCast) { + translateExpressionInternal(typecast.value) + when(typecast.value.type) { DataType.UBYTE, DataType.BOOL -> { when(typecast.type) { DataType.UBYTE, DataType.BYTE -> {} @@ -197,12 +197,12 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateExpression(expr: AddressOf) { + private fun translateExpression(expr: PtAddressOf) { val name = asmgen.asmVariableName(expr.identifier) asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex") } - private fun translateExpression(expr: NumericLiteral) { + private fun translateExpression(expr: PtNumber) { when(expr.type) { DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex") DataType.UWORD, DataType.WORD -> asmgen.out(""" @@ -220,9 +220,9 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateExpression(expr: IdentifierReference) { + private fun translateExpression(expr: PtIdentifier) { val varname = asmgen.asmVariableName(expr) - when(expr.inferType(program).getOr(DataType.UNDEFINED)) { + when(expr.type) { DataType.UBYTE, DataType.BYTE -> { asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex") } @@ -239,22 +239,17 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateExpression(expr: BinaryExpression) { + private fun translateExpression(expr: PtBinaryExpression) { // Uses evalstack to evaluate the given expression. // TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance). - val leftIDt = expr.left.inferType(program) - val rightIDt = expr.right.inferType(program) - if(!leftIDt.isKnown || !rightIDt.isKnown) - throw AssemblyError("can't infer type of both expression operands") - - val leftDt = leftIDt.getOrElse { throw AssemblyError("unknown dt") } - val rightDt = rightIDt.getOrElse { throw AssemblyError("unknown dt") } + val leftDt = expr.left.type + val rightDt = expr.right.type // see if we can apply some optimized routines when(expr.operator) { "+" -> { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { - val leftVal = expr.left.constValue(program)?.number?.toInt() - val rightVal = expr.right.constValue(program)?.number?.toInt() + val leftVal = expr.left.asConstInteger() + val rightVal = expr.right.asConstInteger() if (leftVal!=null && leftVal in -4..4) { translateExpressionInternal(expr.right) if(rightDt in ByteDatatypes) { @@ -318,7 +313,7 @@ internal class ExpressionsAsmGen(private val program: Program, } "-" -> { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { - val rightVal = expr.right.constValue(program)?.number?.toInt() + val rightVal = expr.right.asConstInteger() if (rightVal!=null && rightVal in -4..4) { translateExpressionInternal(expr.left) @@ -352,7 +347,7 @@ internal class ExpressionsAsmGen(private val program: Program, } } ">>" -> { - val amount = expr.right.constValue(program)?.number?.toInt() + val amount = expr.right.asConstInteger() if(amount!=null) { translateExpressionInternal(expr.left) when (leftDt) { @@ -423,7 +418,7 @@ internal class ExpressionsAsmGen(private val program: Program, } } "<<" -> { - val amount = expr.right.constValue(program)?.number?.toInt() + val amount = expr.right.asConstInteger() if(amount!=null) { translateExpressionInternal(expr.left) if (leftDt in ByteDatatypes) { @@ -450,13 +445,13 @@ internal class ExpressionsAsmGen(private val program: Program, } "*" -> { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { - val leftVar = expr.left as? IdentifierReference - val rightVar = expr.right as? IdentifierReference + val leftVar = expr.left as? PtIdentifier + val rightVar = expr.right as? PtIdentifier if(leftVar!=null && rightVar!=null && leftVar==rightVar) return translateSquared(leftVar, leftDt) } - val value = expr.right.constValue(program) + val value = expr.right as? PtNumber if(value!=null) { if(rightDt in IntegerDatatypes) { val amount = value.number.toInt() @@ -516,7 +511,7 @@ internal class ExpressionsAsmGen(private val program: Program, } "/" -> { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { - val rightVal = expr.right.constValue(program)?.number?.toInt() + val rightVal = expr.right.asConstInteger() if(rightVal!=null && rightVal==2) { translateExpressionInternal(expr.left) when (leftDt) { @@ -557,8 +552,8 @@ internal class ExpressionsAsmGen(private val program: Program, } in ComparisonOperators -> { if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) { - val rightVal = expr.right.constValue(program)?.number - if(rightVal==0.0) + val rightVal = expr.right.asConstInteger() + if(rightVal==0) return translateComparisonWithZero(expr.left, leftDt, expr.operator) } } @@ -584,8 +579,8 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) { - if(expr.isSimple) { + private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) { + if(expr.isSimple()) { if(operator=="!=") { when (dt) { in ByteDatatypes -> { @@ -641,7 +636,7 @@ internal class ExpressionsAsmGen(private val program: Program, } "<" -> { if(dt==DataType.UBYTE || dt==DataType.UWORD) - return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position)) + return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position)) when(dt) { DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b") DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w") @@ -671,7 +666,7 @@ internal class ExpressionsAsmGen(private val program: Program, } ">=" -> { if(dt==DataType.UBYTE || dt==DataType.UWORD) - return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position)) + return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position)) when(dt) { DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb") DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw") @@ -683,7 +678,7 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateSquared(variable: IdentifierReference, dt: DataType) { + private fun translateSquared(variable: PtIdentifier, dt: DataType) { val asmVar = asmgen.asmVariableName(variable) when(dt) { DataType.BYTE, DataType.UBYTE -> { @@ -699,14 +694,12 @@ internal class ExpressionsAsmGen(private val program: Program, asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") } - private fun translateExpression(expr: PrefixExpression) { - translateExpressionInternal(expr.expression) - val itype = expr.inferType(program) - val type = itype.getOrElse { throw AssemblyError("unknown dt") } + private fun translateExpression(expr: PtPrefix) { + translateExpressionInternal(expr.value) when(expr.operator) { "+" -> {} "-" -> { - when(type) { + when(expr.type) { in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b") in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w") DataType.FLOAT -> asmgen.out(" jsr floats.neg_f") @@ -714,7 +707,7 @@ internal class ExpressionsAsmGen(private val program: Program, } } "~" -> { - when(type) { + when(expr.type) { in ByteDatatypes -> asmgen.out(""" lda P8ESTACK_LO+1,x @@ -729,22 +722,18 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateExpression(arrayExpr: ArrayIndexedExpression) { - val elementIDt = arrayExpr.inferType(program) - if(!elementIDt.isKnown) - throw AssemblyError("unknown dt") - val elementDt = elementIDt.getOr(DataType.UNDEFINED) - val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar) + private fun translateExpression(arrayExpr: PtArrayIndexer) { + val elementDt = arrayExpr.type + val arrayVarName = asmgen.asmVariableName(arrayExpr.variable) - val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!! - if(arrayVarDecl.datatype==DataType.UWORD) { + if(arrayExpr.variable.type==DataType.UWORD) { // indexing a pointer var instead of a real array or string if(elementDt !in ByteDatatypes) throw AssemblyError("non-array var indexing requires bytes dt") - if(arrayExpr.inferType(program) isnot DataType.UBYTE) + if(arrayExpr.index.type != DataType.UBYTE) throw AssemblyError("non-array var indexing requires bytes index") asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y) - if(asmgen.isZpVar(arrayExpr.arrayvar)) { + if(asmgen.isZpVar(arrayExpr.variable)) { asmgen.out(" lda ($arrayVarName),y") } else { asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1") @@ -754,7 +743,7 @@ internal class ExpressionsAsmGen(private val program: Program, return } - val constIndexNum = arrayExpr.indexer.constIndex() + val constIndexNum = arrayExpr.index.asConstInteger() if(constIndexNum!=null) { val indexValue = constIndexNum * program.memsizer.memorySize(elementDt) when(elementDt) { @@ -881,7 +870,7 @@ internal class ExpressionsAsmGen(private val program: Program, } } - private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) { + private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) { asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null) asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null) asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt new file mode 100644 index 000000000..cda0c0942 --- /dev/null +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt @@ -0,0 +1,135 @@ +package prog8.codegen.cpu6502 + +import prog8.code.ast.* +import prog8.code.core.* +import kotlin.math.abs + +// TODO include this in the node class directly? + +internal fun PtExpression.asConstInteger(): Int? = + (this as? PtNumber)?.number?.toInt() + + +internal fun PtRange.toConstantIntegerRange(): IntProgression? { + fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression { + return when { + fromVal <= toVal -> when { + stepVal <= 0 -> IntRange.EMPTY + stepVal == 1 -> fromVal..toVal + else -> fromVal..toVal step stepVal + } + else -> when { + stepVal >= 0 -> IntRange.EMPTY + stepVal == -1 -> fromVal downTo toVal + else -> fromVal downTo toVal step abs(stepVal) + } + } + } + + val fromLv = from as? PtNumber + val toLv = to as? PtNumber + val stepLv = step as? PtNumber + if(fromLv==null || toLv==null || stepLv==null) + return null + val fromVal = fromLv.number.toInt() + val toVal = toLv.number.toInt() + val stepVal = stepLv.number.toInt() + return makeRange(fromVal, toVal, stepVal) +} + + +fun PtExpression.isSimple(): Boolean { + return when(this) { + is PtAddressOf -> true + is PtArray -> true + is PtArrayIndexer -> index is PtNumber || index is PtIdentifier + is PtBinaryExpression -> false + is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") + is PtContainmentCheck -> false + is PtFunctionCall -> false + is PtIdentifier -> true + is PtMachineRegister -> true + is PtMemoryByte -> address is PtNumber || address is PtIdentifier + is PtNumber -> true + is PtPrefix -> value.isSimple() + is PtRange -> true + is PtString -> true + is PtTypeCast -> value.isSimple() + } +} + +internal fun IPtSubroutine.regXasResult(): Boolean = + (this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } + +internal fun IPtSubroutine.shouldSaveX(): Boolean = + this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam())) + +internal fun PtAsmSub.regXasParam(): Boolean = + parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } + +internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean) + +internal fun PtAsmSub.shouldKeepA(): KeepAresult { + // determine if A's value should be kept when preparing for calling the subroutine, and when returning from it + + // it seems that we never have to save A when calling? will be loaded correctly after setup. + // but on return it depends on wether the routine returns something in A. + val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX } + return KeepAresult(false, saveAonReturn) +} + +internal fun IPtSubroutine.returnsWhatWhere(): List> { + when(this) { + is PtAsmSub -> { + return returns + } + is PtSub -> { + // for non-asm subroutines, determine the return registers based on the type of the return value + return if(returntype==null) + emptyList() + else { + val register = when (returntype!!) { + in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null) + in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) + DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) + else -> RegisterOrStatusflag(RegisterOrPair.AY, null) + } + listOf(Pair(register, returntype!!)) + } + } + } +} + +internal fun PtExpression.clone(): PtExpression { + fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression { + orig.children.forEach { clone.add((it as PtExpression).clone()) } + return clone + } + when(this) { + is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position)) + is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position)) + is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position)) + is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position)) + is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position)) + is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position)) + is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position)) + is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position)) + is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position)) + is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position)) + is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position)) + is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position)) + is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position)) + is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position)) + is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position)) + } +} + +internal fun PtSub.returnRegister(): RegisterOrStatusflag? { + return when(returntype) { + in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null) + in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) + DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) + null -> null + else -> RegisterOrStatusflag(RegisterOrPair.AY, null) + } +} diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt index f3cdb6084..f4b52b780 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt @@ -1,46 +1,43 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold -import prog8.ast.Program -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.RangeExpression -import prog8.ast.statements.ForLoop +import prog8.code.ast.* import prog8.code.core.* import kotlin.math.absoluteValue -internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) { +internal class ForLoopsAsmGen(private val program: PtProgram, + private val asmgen: AsmGen6502Internal, + private val zeropage: Zeropage) { - internal fun translate(stmt: ForLoop) { - val iterableDt = stmt.iterable.inferType(program) - if(!iterableDt.isKnown) - throw AssemblyError("unknown dt") + internal fun translate(stmt: PtForLoop) { + val iterableDt = stmt.iterable.type when(stmt.iterable) { - is RangeExpression -> { - val range = (stmt.iterable as RangeExpression).toConstantIntegerRange() + is PtRange -> { + val range = (stmt.iterable as PtRange).toConstantIntegerRange() if(range==null) { - translateForOverNonconstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as RangeExpression) + translateForOverNonconstRange(stmt, iterableDt, stmt.iterable as PtRange) } else { - translateForOverConstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, range) + translateForOverConstRange(stmt, iterableDt, range) } } - is IdentifierReference -> { - translateForOverIterableVar(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as IdentifierReference) + is PtIdentifier -> { + translateForOverIterableVar(stmt, iterableDt, stmt.iterable as PtIdentifier) } else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable") } } - private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") - val modifiedLabel = program.makeLabel("for_modified") - val modifiedLabel2 = program.makeLabel("for_modifiedb") + private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") + val modifiedLabel = asmgen.makeLabel("for_modified") + val modifiedLabel2 = asmgen.makeLabel("for_modifiedb") asmgen.loopEndLabels.push(endLabel) - val stepsize=range.step.constValue(program)!!.number.toInt() + val stepsize=range.step.asConstInteger()!! if(stepsize < -1) { - val limit = range.to.constValue(program)?.number - if(limit==0.0) + val limit = range.to.asConstInteger() + if(limit==0) throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping") } @@ -52,11 +49,11 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen: val incdec = if(stepsize==1) "inc" else "dec" // loop over byte range via loopvar - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null) asmgen.out(loopLabel) - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) asmgen.out(""" lda $varname $modifiedLabel cmp #0 ; modified @@ -70,11 +67,11 @@ $modifiedLabel cmp #0 ; modified // bytes, step >= 2 or <= -2 // loop over byte range via loopvar - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null) asmgen.out(loopLabel) - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) if(stepsize>0) { asmgen.out(""" lda $varname @@ -102,14 +99,14 @@ $modifiedLabel cmp #0 ; modified // words, step 1 or -1 stepsize == 1 || stepsize == -1 -> { - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) assignLoopvar(stmt, range) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.out(""" sty $modifiedLabel+1 sta $modifiedLabel2+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) asmgen.out(""" lda $varname+1 $modifiedLabel cmp #0 ; modified @@ -136,14 +133,14 @@ $modifiedLabel2 cmp #0 ; modified stepsize > 0 -> { // (u)words, step >= 2 - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) assignLoopvar(stmt, range) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.out(""" sty $modifiedLabel+1 sta $modifiedLabel2+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) if (iterableDt == DataType.ARRAY_UW) { asmgen.out(""" @@ -184,14 +181,14 @@ $endLabel""") else -> { // (u)words, step <= -2 - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) assignLoopvar(stmt, range) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.out(""" sty $modifiedLabel+1 sta $modifiedLabel2+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) if(iterableDt==DataType.ARRAY_UW) { asmgen.out(""" @@ -237,12 +234,18 @@ $endLabel""") asmgen.loopEndLabels.pop() } - private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) val iterableName = asmgen.asmVariableName(ident) - val decl = ident.targetVarDecl(program)!! + val symbol = asmgen.symbolTable.lookup(ident.name) + val decl = symbol!!.astNode as IPtVariable + val numElements = when(decl) { + is PtConstant -> throw AssemblyError("length of non-array requested") + is PtMemMapped -> decl.arraySize + is PtVariable -> decl.arraySize + } when(iterableDt) { DataType.STR -> { asmgen.out(""" @@ -252,8 +255,8 @@ $endLabel""") sty $loopLabel+2 $loopLabel lda ${65535.toHex()} ; modified beq $endLabel - sta ${asmgen.asmVariableName(stmt.loopVar)}""") - asmgen.translate(stmt.body) + sta ${asmgen.asmVariableName(stmt.variable)}""") + asmgen.translate(stmt.statements) asmgen.out(""" inc $loopLabel+1 bne $loopLabel @@ -262,19 +265,18 @@ $loopLabel lda ${65535.toHex()} ; modified $endLabel""") } DataType.ARRAY_UB, DataType.ARRAY_B -> { - val length = decl.arraysize!!.constIndex()!! - val indexVar = program.makeLabel("for_index") + val indexVar = asmgen.makeLabel("for_index") asmgen.out(""" ldy #0 $loopLabel sty $indexVar lda $iterableName,y - sta ${asmgen.asmVariableName(stmt.loopVar)}""") - asmgen.translate(stmt.body) - if(length<=255) { + sta ${asmgen.asmVariableName(stmt.variable)}""") + asmgen.translate(stmt.statements) + if(numElements!!<=255u) { asmgen.out(""" ldy $indexVar iny - cpy #$length + cpy #$numElements beq $endLabel bne $loopLabel""") } else { @@ -285,7 +287,7 @@ $loopLabel sty $indexVar bne $loopLabel beq $endLabel""") } - if(length>=16) { + if(numElements>=16u) { // allocate index var on ZP if possible val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors) result.fold( @@ -298,9 +300,9 @@ $loopLabel sty $indexVar asmgen.out(endLabel) } DataType.ARRAY_W, DataType.ARRAY_UW -> { - val length = decl.arraysize!!.constIndex()!! * 2 - val indexVar = program.makeLabel("for_index") - val loopvarName = asmgen.asmVariableName(stmt.loopVar) + val length = numElements!! * 2u + val indexVar = asmgen.makeLabel("for_index") + val loopvarName = asmgen.asmVariableName(stmt.variable) asmgen.out(""" ldy #0 $loopLabel sty $indexVar @@ -308,8 +310,8 @@ $loopLabel sty $indexVar sta $loopvarName lda $iterableName+1,y sta $loopvarName+1""") - asmgen.translate(stmt.body) - if(length<=127) { + asmgen.translate(stmt.statements) + if(length<=127u) { asmgen.out(""" ldy $indexVar iny @@ -326,7 +328,7 @@ $loopLabel sty $indexVar bne $loopLabel beq $endLabel""") } - if(length>=16) { + if(length>=16u) { // allocate index var on ZP if possible val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors) result.fold( @@ -346,7 +348,7 @@ $loopLabel sty $indexVar asmgen.loopEndLabels.pop() } - private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) { + private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) { if (range.isEmpty() || range.step==0) throw AssemblyError("empty range or step 0") if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) { @@ -359,18 +361,18 @@ $loopLabel sty $indexVar } // not one of the easy cases, generate more complex code... - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) when(iterableDt) { DataType.ARRAY_B, DataType.ARRAY_UB -> { // loop over byte range via loopvar, step >= 2 or <= -2 - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.out(""" lda #${range.first} sta $varname $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) when (range.step) { 0, 1, -1 -> { throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt") @@ -430,7 +432,7 @@ $loopLabel""") } DataType.ARRAY_W, DataType.ARRAY_UW -> { // loop over word range via loopvar, step >= 2 or <= -2 - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) when (range.step) { 0, 1, -1 -> { throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt") @@ -444,7 +446,7 @@ $loopLabel""") sta $varname sty $varname+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) asmgen.out(""" lda $varname cmp #<${range.last} @@ -470,16 +472,16 @@ $loopLabel""") asmgen.loopEndLabels.pop() } - private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.out(""" lda #${range.first} sta $varname $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) if (range.last == 255) { asmgen.out(""" inc $varname @@ -496,16 +498,16 @@ $endLabel""") asmgen.loopEndLabels.pop() } - private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.out(""" lda #${range.first} sta $varname $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) when (range.last) { 0 -> { asmgen.out(""" @@ -533,18 +535,18 @@ $endLabel""") asmgen.loopEndLabels.pop() } - private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.out(""" lda #<${range.first} ldy #>${range.first} sta $varname sty $varname+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) asmgen.out(""" lda $varname cmp #<${range.last} @@ -560,18 +562,18 @@ $loopLabel""") asmgen.loopEndLabels.pop() } - private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) { - val loopLabel = program.makeLabel("for_loop") - val endLabel = program.makeLabel("for_end") + private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) { + val loopLabel = asmgen.makeLabel("for_loop") + val endLabel = asmgen.makeLabel("for_end") asmgen.loopEndLabels.push(endLabel) - val varname = asmgen.asmVariableName(stmt.loopVar) + val varname = asmgen.asmVariableName(stmt.variable) asmgen.out(""" lda #<${range.first} ldy #>${range.first} sta $varname sty $varname+1 $loopLabel""") - asmgen.translate(stmt.body) + asmgen.translate(stmt.statements) asmgen.out(""" lda $varname cmp #<${range.last} @@ -588,10 +590,10 @@ $loopLabel""") asmgen.loopEndLabels.pop() } - private fun assignLoopvar(stmt: ForLoop, range: RangeExpression) = + private fun assignLoopvar(stmt: PtForLoop, range: PtRange) = asmgen.assignExpressionToVariable( range.from, - asmgen.asmVariableName(stmt.loopVar), - stmt.loopVarDt(program).getOrElse { throw AssemblyError("unknown dt") }, - stmt.definingSubroutine) + asmgen.asmVariableName(stmt.variable), + stmt.variable.type, + stmt.definingISub()) } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index 101b13d76..d0c0e9c5c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -1,16 +1,6 @@ package prog8.codegen.cpu6502 -import prog8.ast.IFunctionCall -import prog8.ast.Node -import prog8.ast.Program -import prog8.ast.expressions.AddressOf -import prog8.ast.expressions.Expression -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.NumericLiteral -import prog8.ast.statements.FunctionCallStatement -import prog8.ast.statements.InlineAssembly -import prog8.ast.statements.Subroutine -import prog8.ast.statements.SubroutineParameter +import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.assignment.AsmAssignSource import prog8.codegen.cpu6502.assignment.AsmAssignTarget @@ -18,51 +8,60 @@ import prog8.codegen.cpu6502.assignment.AsmAssignment import prog8.codegen.cpu6502.assignment.TargetStorageKind -internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { +internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) { - internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) { + internal fun translateFunctionCallStatement(stmt: PtFunctionCall) { saveXbeforeCall(stmt) - translateFunctionCall(stmt, false) + translateFunctionCall(stmt) restoreXafterCall(stmt) // just ignore any result values from the function call. } - internal fun saveXbeforeCall(stmt: IFunctionCall) { - val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") + internal fun saveXbeforeCall(stmt: PtFunctionCall) { + val symbol = asmgen.symbolTable.lookup(stmt.name) + val sub = symbol!!.astNode as IPtSubroutine if(sub.shouldSaveX()) { - val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls - if(regSaveOnStack) - asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry) - else - asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!) + if(sub is PtAsmSub) { + val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls + if (regSaveOnStack) + asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry) + else + asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!) + } else + asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!) } } - internal fun restoreXafterCall(stmt: IFunctionCall) { - val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") + internal fun restoreXafterCall(stmt: PtFunctionCall) { + val symbol = asmgen.symbolTable.lookup(stmt.name) + val sub = symbol!!.astNode as IPtSubroutine if(sub.shouldSaveX()) { - val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls - if(regSaveOnStack) - asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn) - else + if(sub is PtAsmSub) { + val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls + if (regSaveOnStack) + asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn) + else + asmgen.restoreRegisterLocal(CpuRegister.X) + } else asmgen.restoreRegisterLocal(CpuRegister.X) } } - internal fun optimizeIntArgsViaRegisters(sub: Subroutine) = + internal fun optimizeIntArgsViaRegisters(sub: PtSub) = (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) - internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter + internal fun translateFunctionCall(call: PtFunctionCall) { // Output only the code to set up the parameters and perform the actual call // NOTE: does NOT output the code to deal with the result values! // NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!! // (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this) - val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}") - val subAsmName = asmgen.asmSymbolName(call.target) + val symbol = asmgen.symbolTable.lookup(call.name) + val sub = symbol!!.astNode as IPtSubroutine + val subAsmName = asmgen.asmSymbolName(call.name) - if(sub.isAsmSubroutine) { + if(sub is PtAsmSub) { argumentsViaRegisters(sub, call) if (sub.inline && asmgen.options.optimize) { // inline the subroutine. @@ -70,16 +69,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine // (this condition has been enforced by an ast check earlier) asmgen.out(" \t; inlined routine follows: ${sub.name}") - sub.statements.forEach { asmgen.translate(it as InlineAssembly) } + sub.children.forEach { asmgen.translate(it as PtInlineAssembly) } asmgen.out(" \t; inlined routine end: ${sub.name}") } else { asmgen.out(" jsr $subAsmName") } } - else { - if(sub.inline) - throw AssemblyError("can only reliably inline asmsub routines at this time") - + else if(sub is PtSub) { if(optimizeIntArgsViaRegisters(sub)) { if(sub.parameters.size==1) { val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY @@ -100,84 +96,82 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } asmgen.out(" jsr $subAsmName") } + else throw AssemblyError("invalid sub type") // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller } - private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) { + private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) { if(sub.parameters.size==1) { - argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0]) + argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0]) } else { - if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) { + if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) { registerArgsViaCpuStackEvaluation(call, sub) } else { asmsub6502ArgsEvalOrder(sub).forEach { val param = sub.parameters[it] val arg = call.args[it] - argumentViaRegister(sub, IndexedValue(it, param), arg) + argumentViaRegister(sub, IndexedValue(it, param.second), arg) } } } } - private fun registerArgsViaCpuStackEvaluation(call: IFunctionCall, callee: Subroutine) { + private fun registerArgsViaCpuStackEvaluation(call: PtFunctionCall, callee: PtAsmSub) { // this is called when one or more of the arguments are 'complex' and // cannot be assigned to a register easily or risk clobbering other registers. - require(callee.isAsmSubroutine) { "register args only for asm subroutine ${callee.position}" } if(callee.parameters.isEmpty()) return // use the cpu hardware stack as intermediate storage for the arguments. val argOrder = asmsub6502ArgsEvalOrder(callee) argOrder.reversed().forEach { - asmgen.pushCpuStack(callee.parameters[it].type, call.args[it]) + asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it]) } argOrder.forEach { val param = callee.parameters[it] - val targetVar = callee.searchParameter(param.name)!! - asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine) + asmgen.popCpuStack(callee, param.second, param.first) } } - private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) { + private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) { // pass parameter via a regular variable (not via registers) - val valueIDt = value.inferType(program) - val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } - if(!isArgumentTypeCompatible(valueDt, parameter.type)) + if(!isArgumentTypeCompatible(value.type, parameter.type)) throw AssemblyError("argument type incompatible") - val varName = asmgen.asmVariableName(sub.scopedName + parameter.name) + val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name) asmgen.assignExpressionToVariable(value, varName, parameter.type, sub) } - private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue, value: Expression, registerOverride: RegisterOrPair? = null) { + private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue, value: PtExpression, registerOverride: RegisterOrPair? = null) { // pass argument via a register parameter - val valueIDt = value.inferType(program) - val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } - if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) + if(!isArgumentTypeCompatible(value.type, parameter.value.type)) throw AssemblyError("argument type incompatible") - val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null) + val paramRegister: RegisterOrStatusflag = when(sub) { + is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null) + is PtSub -> RegisterOrStatusflag(registerOverride!!, null) + } val statusflag = paramRegister.statusflag val register = paramRegister.registerOrPair val requiredDt = parameter.value.type - if(requiredDt!=valueDt) { - if(valueDt largerThan requiredDt) + if(requiredDt!=value.type) { + if(value.type largerThan requiredDt) throw AssemblyError("can only convert byte values to word param types") } if (statusflag!=null) { - if(requiredDt!=valueDt) + if(requiredDt!=value.type) throw AssemblyError("for statusflag, byte value is required") if (statusflag == Statusflag.Pc) { // this param needs to be set last, right before the jsr // for now, this is already enforced on the subroutine definition by the Ast Checker when(value) { - is NumericLiteral -> { + is PtNumber -> { val carrySet = value.number.toInt() != 0 asmgen.out(if(carrySet) " sec" else " clc") } - is IdentifierReference -> { + is PtIdentifier -> { val sourceName = asmgen.asmVariableName(value) asmgen.out(""" pha @@ -202,10 +196,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg else { // via register or register pair register!! - if(requiredDt largerThan valueDt) { + if(requiredDt largerThan value.type) { // we need to sign extend the source, do this via temporary word variable asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub) - asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt) + asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type) asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register) } else { val target: AsmAssignTarget = @@ -215,9 +209,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD AsmAssignTarget.fromRegisters(register, signed, sub, asmgen) } - val src = if(valueDt in PassByReferenceDatatypes) { - if(value is IdentifierReference) { - val addr = AddressOf(value, Position.DUMMY) + val src = if(value.type in PassByReferenceDatatypes) { + if(value is PtIdentifier) { + val addr = PtAddressOf(Position.DUMMY) + addr.add(value) + addr.parent = sub as PtNode AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) } else { AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt index d5d052725..4991b2d0b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt @@ -1,23 +1,23 @@ package prog8.codegen.cpu6502 -import prog8.ast.Program -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.NumericLiteral -import prog8.ast.statements.PostIncrDecr +import prog8.code.ast.PtIdentifier +import prog8.code.ast.PtNumber +import prog8.code.ast.PtPostIncrDecr +import prog8.code.ast.PtProgram import prog8.code.core.* -internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) { - internal fun translate(stmt: PostIncrDecr) { +internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) { + internal fun translate(stmt: PtPostIncrDecr) { val incr = stmt.operator=="++" val targetIdent = stmt.target.identifier - val targetMemory = stmt.target.memoryAddress - val targetArrayIdx = stmt.target.arrayindexed - val scope = stmt.definingSubroutine + val targetMemory = stmt.target.memory + val targetArrayIdx = stmt.target.array + val scope = stmt.definingISub() when { targetIdent!=null -> { val what = asmgen.asmVariableName(targetIdent) - when (stmt.target.inferType(program).getOr(DataType.UNDEFINED)) { + when (stmt.target.type) { in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what") in WordDatatypes -> { if(incr) @@ -38,12 +38,12 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg } } targetMemory!=null -> { - when (val addressExpr = targetMemory.addressExpression) { - is NumericLiteral -> { + when (val addressExpr = targetMemory.address) { + is PtNumber -> { val what = addressExpr.number.toHex() asmgen.out(if(incr) " inc $what" else " dec $what") } - is IdentifierReference -> { + is PtIdentifier -> { val what = asmgen.asmVariableName(addressExpr) asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2") if(incr) @@ -62,9 +62,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg } } targetArrayIdx!=null -> { - val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar) - val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED) - val constIndex = targetArrayIdx.indexer.constIndex() + val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable) + val elementDt = targetArrayIdx.type + val constIndex = targetArrayIdx.index.asConstInteger() if(constIndex!=null) { val indexValue = constIndex * program.memsizer.memorySize(elementDt) when(elementDt) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 716c846f0..aead05dea 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -1,12 +1,10 @@ package prog8.codegen.cpu6502 -import prog8.ast.Program -import prog8.ast.statements.* import prog8.code.* +import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.TargetStorageKind -import prog8.compiler.CallGraph import java.time.LocalDate import java.time.LocalDateTime import kotlin.math.absoluteValue @@ -20,30 +18,26 @@ import kotlin.math.absoluteValue * - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!) */ internal class ProgramAndVarsGen( - val program: Program, + val program: PtProgram, val options: CompilationOptions, val errors: IErrorReporter, private val symboltable: SymbolTable, private val functioncallAsmGen: FunctionCallAsmGen, - private val asmgen: AsmGen, + private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator, private val zeropage: Zeropage ) { private val compTarget = options.compTarget - private val callGraph = CallGraph(program, true) - private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance() } + private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance() } internal fun generate() { - val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } - require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"} - header() - val allBlocks = program.allBlocks + val allBlocks = program.allBlocks() if(allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'") if(errors.noErrors()) { - program.allBlocks.forEach { block2asm(it) } + program.allBlocks().forEach { block2asm(it) } // the global list of all floating point constants for the whole program asmgen.out("; global float constants") @@ -100,7 +94,7 @@ internal class ProgramAndVarsGen( when(options.launcher) { CbmPrgLauncherType.BASIC -> { if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) { - errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.toplevelModule.position) + errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position) } asmgen.out("; ---- basic program with sys call ----") asmgen.out("* = ${options.loadAddress.toHex()}") @@ -183,28 +177,28 @@ internal class ProgramAndVarsGen( asmgen.out("prog8_program_end\t; end of program label for progend()") } - private fun block2asm(block: Block) { + private fun block2asm(block: PtBlock) { asmgen.out("") asmgen.out("; ---- block: '${block.name}' ----") if(block.address!=null) asmgen.out("* = ${block.address!!.toHex()}") else { - if("align_word" in block.options()) + if(block.alignment==PtBlock.BlockAlignment.WORD) asmgen.out("\t.align 2") - else if("align_page" in block.options()) + else if(block.alignment==PtBlock.BlockAlignment.PAGE) asmgen.out("\t.align $100") } - asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n")) + asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block\n" else ".proc\n")) asmgen.outputSourceLine(block) createBlockVariables(block) - asmsubs2asm(block.statements) + asmsubs2asm(block.children) asmgen.out("") val initializers = blockVariableInitializers.getValue(block) - val notInitializers = block.statements.filterNot { it in initializers } + val notInitializers = block.children.filterNot { it in initializers } notInitializers.forEach { asmgen.translate(it) } if(!options.dontReinitGlobals) { @@ -216,13 +210,13 @@ internal class ProgramAndVarsGen( } } - asmgen.out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n") + asmgen.out(if(block.forceOutput) "\n\t.bend\n" else "\n\t.pend\n") } private fun getVars(scope: StNode): Map = scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) } - private fun createBlockVariables(block: Block) { + private fun createBlockVariables(block: PtBlock) { val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") } require(scope.type==StNodeType.BLOCK) val varsInBlock = getVars(scope) @@ -247,18 +241,10 @@ internal class ProgramAndVarsGen( nonZpVariables2asm(variables) } - internal fun translateSubroutine(sub: Subroutine) { - var onlyVariables = false - + internal fun translateAsmSubroutine(sub: PtAsmSub) { if(sub.inline) { if(options.optimize) { - if(sub.isAsmSubroutine || callGraph.unused(sub)) - return - - // from an inlined subroutine only the local variables are generated, - // all other code statements are omitted in the subroutine itself - // (they've been inlined at the call site, remember?) - onlyVariables = true + return } } @@ -266,7 +252,7 @@ internal class ProgramAndVarsGen( val asmStartScope: String val asmEndScope: String - if(sub.definingBlock.options().contains("force_output")) { + if(sub.definingBlock()!!.forceOutput) { asmStartScope = ".block" asmEndScope = ".bend" } else { @@ -274,95 +260,105 @@ internal class ProgramAndVarsGen( asmEndScope = ".pend" } - if(sub.isAsmSubroutine) { - if(sub.asmAddress!=null) - return // already done at the memvars section + if(sub.address!=null) + return // already done at the memvars section - // asmsub with most likely just an inline asm in it - asmgen.out("${sub.name}\t$asmStartScope") - sub.statements.forEach { asmgen.translate(it) } - asmgen.out(" $asmEndScope\n") + // asmsub with most likely just an inline asm in it + asmgen.out("${sub.name}\t$asmStartScope") + sub.children.forEach { asmgen.translate(it) } + asmgen.out(" $asmEndScope\n") + } + + + internal fun translateSubroutine(sub: PtSub) { + asmgen.out("") + + val asmStartScope: String + val asmEndScope: String + if(sub.definingBlock()!!.forceOutput) { + asmStartScope = ".block" + asmEndScope = ".bend" } else { - // regular subroutine - asmgen.out("${sub.name}\t$asmStartScope") - - val scope = symboltable.lookupOrElse(sub.scopedName.joinToString(".")) { throw AssemblyError("lookup") } - require(scope.type==StNodeType.SUBROUTINE) - val varsInSubroutine = getVars(scope) - - // Zeropage Variables - val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet() - zeropagevars2asm(varnames) - - // MemDefs and Consts - val mvs = varsInSubroutine - .filter { it.value.type==StNodeType.MEMVAR } - .map { it.value as StMemVar } - val consts = varsInSubroutine - .filter { it.value.type==StNodeType.CONSTANT } - .map { it.value as StConstant } - memdefsAndConsts2asm(mvs, consts) - - asmsubs2asm(sub.statements) - - // the main.start subroutine is the program's entrypoint and should perform some initialization logic - if(sub.name=="start" && sub.definingBlock.name=="main") - entrypointInitialization() - - if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { - asmgen.out("; simple int arg(s) passed via register(s)") - if(sub.parameters.size==1) { - val dt = sub.parameters[0].type - val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name) - if(dt in ByteDatatypes) - asmgen.assignRegister(RegisterOrPair.A, target) - else - asmgen.assignRegister(RegisterOrPair.AY, target) - } else { - require(sub.parameters.size==2) - // 2 simple byte args, first in A, second in Y - val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name) - val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name) - asmgen.assignRegister(RegisterOrPair.A, target1) - asmgen.assignRegister(RegisterOrPair.Y, target2) - } - } - - if(!onlyVariables) { - asmgen.out("; statements") - sub.statements.forEach { asmgen.translate(it) } - } - - asmgen.out("; variables") - val asmGenInfo = asmgen.subroutineExtra(sub) - for((dt, name, addr) in asmGenInfo.extraVars) { - if(addr!=null) - asmgen.out("$name = $addr") - else when(dt) { - DataType.UBYTE -> asmgen.out("$name .byte 0") - DataType.UWORD -> asmgen.out("$name .word 0") - else -> throw AssemblyError("weird dt") - } - } - if(asmGenInfo.usedRegsaveA) // will probably never occur - asmgen.out("prog8_regsaveA .byte 0") - if(asmGenInfo.usedRegsaveX) - asmgen.out("prog8_regsaveX .byte 0") - if(asmGenInfo.usedRegsaveY) - asmgen.out("prog8_regsaveY .byte 0") - if(asmGenInfo.usedFloatEvalResultVar1) - asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0") - if(asmGenInfo.usedFloatEvalResultVar2) - asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0") - - // normal statically allocated variables - val variables = varsInSubroutine - .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) } - .map { it.value as StStaticVariable } - nonZpVariables2asm(variables) - - asmgen.out(" $asmEndScope\n") + asmStartScope = ".proc" + asmEndScope = ".pend" } + + asmgen.out("${sub.name}\t$asmStartScope") + + val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") } + require(scope.type==StNodeType.SUBROUTINE) + val varsInSubroutine = getVars(scope) + + // Zeropage Variables + val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet() + zeropagevars2asm(varnames) + + // MemDefs and Consts + val mvs = varsInSubroutine + .filter { it.value.type==StNodeType.MEMVAR } + .map { it.value as StMemVar } + val consts = varsInSubroutine + .filter { it.value.type==StNodeType.CONSTANT } + .map { it.value as StConstant } + memdefsAndConsts2asm(mvs, consts) + + asmsubs2asm(sub.children) + + // the main.start subroutine is the program's entrypoint and should perform some initialization logic + if(sub.name=="start" && sub.definingBlock()!!.name=="main") + entrypointInitialization() + + if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { + asmgen.out("; simple int arg(s) passed via register(s)") + if(sub.parameters.size==1) { + val dt = sub.parameters[0].type + val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name) + if(dt in ByteDatatypes) + asmgen.assignRegister(RegisterOrPair.A, target) + else + asmgen.assignRegister(RegisterOrPair.AY, target) + } else { + require(sub.parameters.size==2) + // 2 simple byte args, first in A, second in Y + val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name) + val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name) + asmgen.assignRegister(RegisterOrPair.A, target1) + asmgen.assignRegister(RegisterOrPair.Y, target2) + } + } + + asmgen.out("; statements") + sub.children.forEach { asmgen.translate(it) } + + asmgen.out("; variables") + val asmGenInfo = asmgen.subroutineExtra(sub) + for((dt, name, addr) in asmGenInfo.extraVars) { + if(addr!=null) + asmgen.out("$name = $addr") + else when(dt) { + DataType.UBYTE -> asmgen.out("$name .byte 0") + DataType.UWORD -> asmgen.out("$name .word 0") + else -> throw AssemblyError("weird dt") + } + } + if(asmGenInfo.usedRegsaveA) // will probably never occur + asmgen.out("prog8_regsaveA .byte 0") + if(asmGenInfo.usedRegsaveX) + asmgen.out("prog8_regsaveX .byte 0") + if(asmGenInfo.usedRegsaveY) + asmgen.out("prog8_regsaveY .byte 0") + if(asmGenInfo.usedFloatEvalResultVar1) + asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0") + if(asmGenInfo.usedFloatEvalResultVar2) + asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0") + + // normal statically allocated variables + val variables = varsInSubroutine + .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) } + .map { it.value as StStaticVariable } + nonZpVariables2asm(variables) + + asmgen.out(" $asmEndScope\n") } private fun entrypointInitialization() { @@ -593,12 +589,12 @@ internal class ProgramAndVarsGen( } } - private fun asmsubs2asm(statements: List) { + private fun asmsubs2asm(statements: List) { statements - .filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null } + .filter { it is PtAsmSub && it.address!=null } .forEach { asmsub -> - asmsub as Subroutine - asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}") + asmsub as PtAsmSub + asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 862c0a6b5..9ad389a3c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, allocateZeropageVariables() } - internal fun isZpVar(scopedName: List) = scopedName in zeropageVars + internal fun isZpVar(scopedName: List) = scopedName in zeropageVars // TODO as dotted string instead of list? internal fun getFloatAsmConst(number: Double): String { val asmName = globalFloatConsts[number] @@ -59,7 +59,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.fold( @@ -67,7 +67,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, numVariablesAllocatedInZP++ }, failure = { - errors.err(it.message!!, variable.position) + errors.err(it.message!!, variable.astNode.position) } ) } @@ -78,7 +78,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.onSuccess { numVariablesAllocatedInZP++ } @@ -98,7 +98,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.onSuccess { numVariablesAllocatedInZP++ } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index 8f01d7611..c0b615274 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -1,13 +1,10 @@ package prog8.codegen.cpu6502.assignment -import prog8.ast.Program -import prog8.ast.expressions.* -import prog8.ast.statements.AssignTarget -import prog8.ast.statements.Assignment -import prog8.ast.statements.DirectMemoryWrite -import prog8.ast.statements.Subroutine +import prog8.code.ast.* import prog8.code.core.* -import prog8.codegen.cpu6502.AsmGen +import prog8.codegen.cpu6502.AsmGen6502Internal +import prog8.codegen.cpu6502.asConstInteger +import prog8.codegen.cpu6502.returnsWhatWhere internal enum class TargetStorageKind { @@ -29,22 +26,22 @@ internal enum class SourceStorageKind { } internal class AsmAssignTarget(val kind: TargetStorageKind, - private val asmgen: AsmGen, + private val asmgen: AsmGen6502Internal, val datatype: DataType, - val scope: Subroutine?, + val scope: IPtSubroutine?, private val variableAsmName: String? = null, - val array: ArrayIndexedExpression? = null, - val memory: DirectMemoryWrite? = null, + val array: PtArrayIndexer? = null, + val memory: PtMemoryByte? = null, val register: RegisterOrPair? = null, - val origAstTarget: AssignTarget? = null + val origAstTarget: PtAssignTarget? = null ) { - val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() } + val constArrayIndexValue by lazy { array?.index?.asConstInteger()?.toUInt() } val asmVarname: String by lazy { if (array == null) variableAsmName!! else - asmgen.asmVariableName(array.arrayvar) + asmgen.asmVariableName(array.variable) } lateinit var origAssign: AsmAssignment @@ -55,33 +52,31 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, } companion object { - fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget { + fun fromAstAssignment(assign: PtAssignment, asmgen: AsmGen6502Internal): AsmAssignTarget { with(assign.target) { - val idt = inferType(program) - val dt = idt.getOrElse { throw AssemblyError("unknown dt") } when { identifier != null -> { - val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter + val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen) if (parameter!=null) { - val sub = parameter.definingSubroutine!! - if (sub.isAsmSubroutine) { - val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)] + val sub = parameter.definingAsmSub() + if (sub!=null) { + val reg = sub.parameters.single { it.second===parameter }.first if(reg.statusflag!=null) throw AssemblyError("can't assign value to processor statusflag directly") else - return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this) + return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this) } } - return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) + return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingISub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) } - arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this) - memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this) + array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this) + memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this) else -> throw AssemblyError("weird target") } } } - fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, asmgen: AsmGen): AsmAssignTarget = + fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget = when(registers) { RegisterOrPair.A, RegisterOrPair.X, @@ -112,69 +107,66 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, } internal class AsmAssignSource(val kind: SourceStorageKind, - private val program: Program, - private val asmgen: AsmGen, + private val program: PtProgram, + private val asmgen: AsmGen6502Internal, val datatype: DataType, private val variableAsmName: String? = null, - val array: ArrayIndexedExpression? = null, - val memory: DirectMemoryRead? = null, + val array: PtArrayIndexer? = null, + val memory: PtMemoryByte? = null, val register: RegisterOrPair? = null, - val number: NumericLiteral? = null, - val expression: Expression? = null + val number: PtNumber? = null, + val expression: PtExpression? = null ) { val asmVarname: String get() = if(array==null) variableAsmName!! else - asmgen.asmVariableName(array.arrayvar) + asmgen.asmVariableName(array.variable) companion object { - fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource { - val cv = value.constValue(program) + fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource { + val cv = value as? PtNumber if(cv!=null) return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return when(value) { - is NumericLiteral -> throw AssemblyError("should have been constant value") - is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation") - is ArrayLiteral -> throw AssemblyError("array literal value should not occur anymore for asm generation") - is IdentifierReference -> { - val parameter = value.targetVarDecl(program)?.subroutineParameter - if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine) + // checked above: is PtNumber -> throw AssemblyError("should have been constant value") + is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation") + is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation") + is PtIdentifier -> { + val parameter = asmgen.findSubroutineParameter(value.name, asmgen) + if(parameter?.definingAsmSub() != null) throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}") - val dt = value.inferType(program).getOr(DataType.UNDEFINED) val varName=asmgen.asmVariableName(value) // special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system - if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) { + if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) { val regStr = varName.lowercase().substring(5) val reg = RegisterOrPair.valueOf(regStr.uppercase()) - AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg) + AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg) } else { - AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName) + AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = varName) } } - is DirectMemoryRead -> { + is PtMemoryByte -> { AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value) } - is ArrayIndexedExpression -> { - val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") } - AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value) + is PtArrayIndexer -> { + AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value) } - is BuiltinFunctionCall -> { - val returnType = value.inferType(program) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value) + is PtBuiltinFunctionCall -> { + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value) } - is FunctionCallExpression -> { - val sub = value.target.targetSubroutine(program)!! - val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first + is PtFunctionCall -> { + val symbol = asmgen.symbolTable.lookup(value.name) + val sub = symbol!!.astNode as IPtSubroutine + val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second ?: throw AssemblyError("can't translate zero return values in assignment") AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) } else -> { - val returnType = value.inferType(program) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value) + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value) } } } @@ -209,7 +201,7 @@ internal class AsmAssignment(val source: AsmAssignSource, if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY)) require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" } require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) { - "source dt size must be less or equal to target dt size at $position" + "source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}" } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 5deb3deef..9bc06cdcc 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1,24 +1,19 @@ package prog8.codegen.cpu6502.assignment -import prog8.ast.Program -import prog8.ast.expressions.* -import prog8.ast.statements.* +import prog8.code.ast.* import prog8.code.core.* -import prog8.codegen.cpu6502.AsmGen -import prog8.codegen.cpu6502.VariableAllocator -import prog8.compiler.builtinFunctionReturnType +import prog8.codegen.cpu6502.* -internal class AssignmentAsmGen(private val program: Program, - private val asmgen: AsmGen, +internal class AssignmentAsmGen(private val program: PtProgram, + private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator) { private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator) - fun translate(assignment: Assignment) { - val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) + fun translate(assignment: PtAssignment) { + val target = AsmAssignTarget.fromAstAssignment(assignment, asmgen) val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) - - val assign = AsmAssignment(source, target, assignment.isAugmentable, program.memsizer, assignment.position) + val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position) target.origAssign = assign if(assign.isAugmentable) @@ -64,17 +59,16 @@ internal class AssignmentAsmGen(private val program: Program, SourceStorageKind.ARRAY -> { val value = assign.source.array!! val elementDt = assign.source.datatype - val arrayVarName = asmgen.asmVariableName(value.arrayvar) + val arrayVarName = asmgen.asmVariableName(value.variable) - val arrayVarDecl = value.arrayvar.targetVarDecl(program)!! - if(arrayVarDecl.datatype==DataType.UWORD) { + if(value.variable.type==DataType.UWORD) { // indexing a pointer var instead of a real array or string if(elementDt !in ByteDatatypes) throw AssemblyError("non-array var indexing requires bytes dt") - if(value.inferType(program) isnot DataType.UBYTE) + if(value.type != DataType.UBYTE) throw AssemblyError("non-array var indexing requires bytes index") asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y) - if(asmgen.isZpVar(value.arrayvar)) { + if(asmgen.isZpVar(value.variable)) { asmgen.out(" lda ($arrayVarName),y") } else { asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1") @@ -84,7 +78,7 @@ internal class AssignmentAsmGen(private val program: Program, return } - val constIndex = value.indexer.constIndex() + val constIndex = value.index.asConstInteger() if (constIndex!=null) { // constant array index value val indexValue = constIndex * program.memsizer.memorySize(elementDt) @@ -133,29 +127,29 @@ internal class AssignmentAsmGen(private val program: Program, } } SourceStorageKind.MEMORY -> { - fun assignViaExprEval(expression: Expression) { + fun assignViaExprEval(expression: PtExpression) { assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") assignRegisterByte(assign.target, CpuRegister.A) } val value = assign.source.memory!! - when (value.addressExpression) { - is NumericLiteral -> { - val address = (value.addressExpression as NumericLiteral).number.toUInt() + when (value.address) { + is PtNumber -> { + val address = (value.address as PtNumber).number.toUInt() assignMemoryByte(assign.target, address, null) } - is IdentifierReference -> { - assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference) + is PtIdentifier -> { + assignMemoryByte(assign.target, null, value.address as PtIdentifier) } - is BinaryExpression -> { - if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) { + is PtBinaryExpression -> { + if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) { assignRegisterByte(assign.target, CpuRegister.A) } else { - assignViaExprEval(value.addressExpression) + assignViaExprEval(value.address) } } - else -> assignViaExprEval(value.addressExpression) + else -> assignViaExprEval(value.address) } } SourceStorageKind.EXPRESSION -> { @@ -173,22 +167,22 @@ internal class AssignmentAsmGen(private val program: Program, private fun assignExpression(assign: AsmAssignment) { when(val value = assign.source.expression!!) { - is AddressOf -> { + is PtAddressOf -> { val sourceName = asmgen.asmSymbolName(value.identifier) assignAddressOf(assign.target, sourceName) } - is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber") - is IdentifierReference -> throw AssemblyError("source kind should have been variable") - is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array") - is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") - is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) - is FunctionCallExpression -> { - val sub = value.target.targetSubroutine(program)!! + is PtNumber -> throw AssemblyError("source kind should have been literalnumber") + is PtIdentifier -> throw AssemblyError("source kind should have been variable") + is PtArrayIndexer -> throw AssemblyError("source kind should have been array") + is PtMemoryByte -> throw AssemblyError("source kind should have been memory") + is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value) + is PtFunctionCall -> { + val symbol = asmgen.symbolTable.lookup(value.name) + val sub = symbol!!.astNode as IPtSubroutine asmgen.saveXbeforeCall(value) - asmgen.translateFunctionCall(value, true) - val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: - sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null } - when (returnValue.first) { + asmgen.translateFunctionCall(value) + val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null } + when (returnValue.second) { DataType.STR -> { asmgen.restoreXafterCall(value) when(assign.target.datatype) { @@ -209,7 +203,7 @@ internal class AssignmentAsmGen(private val program: Program, } else -> { // do NOT restore X register before assigning the result values first - when (returnValue.second.registerOrPair) { + when (returnValue.first.registerOrPair) { RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A) RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X) RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y) @@ -233,7 +227,7 @@ internal class AssignmentAsmGen(private val program: Program, RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14) RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15) else -> { - val sflag = returnValue.second.statusflag + val sflag = returnValue.first.statusflag if(sflag!=null) assignStatusFlagByte(assign.target, sflag) else @@ -245,14 +239,11 @@ internal class AssignmentAsmGen(private val program: Program, } } } - is BuiltinFunctionCall -> { - asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) + is PtBuiltinFunctionCall -> { + val returnDt = asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) if(assign.target.register==null) { // still need to assign the result to the target variable/etc. - val returntype = builtinFunctionReturnType(value.name) - if(!returntype.isKnown) - throw AssemblyError("unknown dt") - when(returntype.getOr(DataType.UNDEFINED)) { + when(returnDt) { in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY DataType.STR -> { @@ -279,13 +270,13 @@ internal class AssignmentAsmGen(private val program: Program, } } } - is PrefixExpression -> { + is PtPrefix -> { if(assign.target.array==null) { // First assign the value to the target then apply the operator in place on the target. // This saves a temporary variable translateNormalAssignment( AsmAssignment( - AsmAssignSource.fromAstSource(value.expression, program, asmgen), + AsmAssignSource.fromAstSource(value.value, program, asmgen), assign.target, false, program.memsizer, assign.position ) @@ -301,11 +292,11 @@ internal class AssignmentAsmGen(private val program: Program, assignPrefixedExpressionToArrayElt(assign) } } - is ContainmentCheck -> { + is PtContainmentCheck -> { containmentCheckIntoA(value) assignRegisterByte(assign.target, CpuRegister.A) } - is BinaryExpression -> { + is PtBinaryExpression -> { if(!attemptAssignOptimizedBinexpr(value, assign)) { // All remaining binary expressions just evaluate via the stack for now. // (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here, @@ -318,7 +309,7 @@ internal class AssignmentAsmGen(private val program: Program, } internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { - require(assign.source.expression is PrefixExpression) + require(assign.source.expression is PtPrefix) if(assign.source.datatype==DataType.FLOAT) { // floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array. assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true) @@ -350,9 +341,9 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean { + private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { if(expr.operator in ComparisonOperators) { - if(expr.right.constValue(program)?.number == 0.0) { + if(expr.right.asConstInteger() == 0) { if(expr.operator == "==" || expr.operator=="!=") { when(assign.target.datatype) { in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true @@ -365,30 +356,37 @@ internal class AssignmentAsmGen(private val program: Program, val origTarget = assign.target.origAstTarget if(origTarget!=null) { assignConstantByte(assign.target, 0) - val assignTrue = AnonymousScope(mutableListOf( - Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position) - ), assign.position) - val assignFalse = AnonymousScope(mutableListOf(), assign.position) - val ifelse = IfElse(expr.copy(), assignTrue, assignFalse, assign.position) - ifelse.linkParents(expr) + val assignTrue = PtNodeGroup() + val assignment = PtAssignment(assign.position) + assignment.add(origTarget) + assignment.add(PtNumber.fromBoolean(true, assign.position)) + assignTrue.add(assignment) + val assignFalse = PtNodeGroup() + val ifelse = PtIfElse(assign.position) + val exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position) + expr.children.forEach { exprClone.children.add(it) } // doesn't seem to need a deep clone + ifelse.add(exprClone) + ifelse.add(assignTrue) + ifelse.add(assignFalse) + ifelse.parent = expr.parent asmgen.translate(ifelse) return true } } - if(!expr.inferType(program).isInteger) + if(expr.type !in IntegerDatatypes) return false fun simpleLogicalBytesExpr() { // both left and right expression operands are simple. - if (expr.right is NumericLiteral || expr.right is IdentifierReference) + if (expr.right is PtNumber || expr.right is PtIdentifier) assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right) - else if (expr.left is NumericLiteral || expr.left is IdentifierReference) + else if (expr.left is PtNumber || expr.left is PtIdentifier) assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left) else { assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.saveRegisterStack(CpuRegister.A, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub()) asmgen.restoreRegisterStack(CpuRegister.A, false) when (expr.operator) { "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") @@ -402,15 +400,15 @@ internal class AssignmentAsmGen(private val program: Program, fun simpleLogicalWordsExpr() { // both left and right expression operands are simple. - if (expr.right is NumericLiteral || expr.right is IdentifierReference) + if (expr.right is PtNumber || expr.right is PtIdentifier) assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right) - else if (expr.left is NumericLiteral || expr.left is IdentifierReference) + else if (expr.left is PtNumber || expr.left is PtIdentifier) assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left) else { assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.Y, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub()) when (expr.operator) { "&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1") "|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1") @@ -422,14 +420,14 @@ internal class AssignmentAsmGen(private val program: Program, } if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) { - if (expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes) { - if (expr.right.isSimple) { + if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { + if (expr.right.isSimple()) { simpleLogicalBytesExpr() return true } } - if (expr.left.inferType(program).isWords && expr.right.inferType(program).isWords) { - if (expr.right.isSimple) { + if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { + if (expr.right.isSimple()) { simpleLogicalWordsExpr() return true } @@ -439,11 +437,11 @@ internal class AssignmentAsmGen(private val program: Program, if(expr.operator == "==" || expr.operator == "!=") { // expression datatype is BOOL (ubyte) but operands can be anything - if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes && - expr.left.isSimple && expr.right.isSimple) { + if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes && + expr.left.isSimple() && expr.right.isSimple()) { assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.saveRegisterStack(CpuRegister.A, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub()) asmgen.restoreRegisterStack(CpuRegister.A, false) if(expr.operator=="==") { asmgen.out(""" @@ -464,12 +462,12 @@ internal class AssignmentAsmGen(private val program: Program, } assignRegisterByte(assign.target, CpuRegister.A) return true - } else if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords && - expr.left.isSimple && expr.right.isSimple) { + } else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes && + expr.left.isSimple() && expr.right.isSimple()) { assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.Y, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub()) asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.A, false) if(expr.operator=="==") { @@ -499,12 +497,12 @@ internal class AssignmentAsmGen(private val program: Program, return false } else if(expr.operator=="+" || expr.operator=="-") { - val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") } + val dt = expr.type val left = expr.left val right = expr.right if(dt in ByteDatatypes) { when (right) { - is IdentifierReference -> { + is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) val symname = asmgen.asmVariableName(right) if(expr.operator=="+") @@ -514,7 +512,7 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterByte(assign.target, CpuRegister.A) return true } - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) if(expr.operator=="+") asmgen.out(" clc | adc #${right.number.toHex()}") @@ -527,7 +525,7 @@ internal class AssignmentAsmGen(private val program: Program, } } else if(dt in WordDatatypes) { when (right) { - is AddressOf -> { + is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) val symbol = asmgen.asmVariableName(right.identifier) if(expr.operator=="+") @@ -551,7 +549,7 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterpairWord(assign.target, RegisterOrPair.AY) return true } - is IdentifierReference -> { + is PtIdentifier -> { val symname = asmgen.asmVariableName(right) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) if(expr.operator=="+") @@ -575,7 +573,7 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterpairWord(assign.target, RegisterOrPair.AY) return true } - is NumericLiteral -> { + is PtNumber -> { assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) if(expr.operator=="+") { asmgen.out(""" @@ -599,10 +597,10 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterpairWord(assign.target, RegisterOrPair.AY) return true } - is TypecastExpression -> { - val castedValue = right.expression - if(right.type in WordDatatypes && castedValue.inferType(program).isBytes) { - if(castedValue is IdentifierReference) { + is PtTypeCast -> { + val castedValue = right.value + if(right.type in WordDatatypes && castedValue.type in ByteDatatypes) { + if(castedValue is PtIdentifier) { val castedSymname = asmgen.asmVariableName(castedValue) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) if(expr.operator=="+") @@ -629,11 +627,11 @@ internal class AssignmentAsmGen(private val program: Program, } } else if(expr.operator=="<<" || expr.operator==">>") { - val shifts = expr.right.constValue(program)?.number?.toInt() + val shifts = expr.right.asConstInteger() if(shifts!=null) { - val dt = expr.left.inferType(program) - if(dt.isBytes && shifts in 0..7) { - val signed = dt istype DataType.BYTE + val dt = expr.left.type + if(dt in ByteDatatypes && shifts in 0..7) { + val signed = dt == DataType.BYTE assignExpressionToRegister(expr.left, RegisterOrPair.A, signed) if(expr.operator=="<<") { repeat(shifts) { @@ -650,8 +648,8 @@ internal class AssignmentAsmGen(private val program: Program, } assignRegisterByte(assign.target, CpuRegister.A) return true - } else if(dt.isWords && shifts in 0..7) { - val signed = dt istype DataType.WORD + } else if(dt in WordDatatypes && shifts in 0..7) { + val signed = dt == DataType.WORD assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed) if(expr.operator=="<<") { if(shifts>0) { @@ -683,11 +681,11 @@ internal class AssignmentAsmGen(private val program: Program, return false } - private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) { + private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.A, false) val operand = when(right) { - is NumericLiteral -> "#${right.number.toHex()}" - is IdentifierReference -> asmgen.asmSymbolName(right) + is PtNumber -> "#${right.number.toHex()}" + is PtIdentifier -> asmgen.asmSymbolName(right) else -> throw AssemblyError("wrong right operand type") } when (operator) { @@ -699,10 +697,10 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterByte(target, CpuRegister.A) } - private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) { + private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.AY, false) when(right) { - is NumericLiteral -> { + is PtNumber -> { val number = right.number.toHex() when (operator) { "&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla") @@ -711,7 +709,7 @@ internal class AssignmentAsmGen(private val program: Program, else -> throw AssemblyError("invalid operator") } } - is IdentifierReference -> { + is PtIdentifier -> { val name = asmgen.asmSymbolName(right) when (operator) { "&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla") @@ -725,10 +723,10 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterpairWord(target, RegisterOrPair.AY) } - private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean { + private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { when (expr.operator) { "==" -> { - when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) { + when(val dt = expr.left.type) { in ByteDatatypes -> { assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE) asmgen.out(""" @@ -765,7 +763,7 @@ internal class AssignmentAsmGen(private val program: Program, } } "!=" -> { - when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) { + when(val dt = expr.left.type) { in ByteDatatypes -> { assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE) asmgen.out(" beq + | lda #1") @@ -807,65 +805,24 @@ internal class AssignmentAsmGen(private val program: Program, assignStackValue(assign.target) } - private fun containmentCheckIntoA(containment: ContainmentCheck) { - val elementDt = containment.element.inferType(program) - val variable = (containment.iterable as? IdentifierReference)?.targetVarDecl(program) - ?: throw AssemblyError("invalid containment iterable type") - - if(variable.origin!=VarDeclOrigin.USERCODE) { - when(variable.datatype) { - DataType.STR -> { - require(elementDt.isBytes) { "must be byte string ${variable.position}" } - val stringVal = variable.value as StringLiteral - val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference) - assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) - asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) - asmgen.restoreRegisterLocal(CpuRegister.A) - asmgen.out(" ldy #${stringVal.value.length}") - asmgen.out(" jsr prog8_lib.containment_bytearray") - return - } - DataType.ARRAY_F -> { - // require(elementDt istype DataType.FLOAT) - throw AssemblyError("containment check of floats not supported") - } - in ArrayDatatypes -> { - require(elementDt.isInteger) { "must be integer array ${variable.position}" } - val arrayVal = variable.value as ArrayLiteral - val dt = elementDt.getOr(DataType.UNDEFINED) - val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference) - when(dt) { - in ByteDatatypes -> { - assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) - asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) - asmgen.restoreRegisterLocal(CpuRegister.A) - asmgen.out(" ldy #${arrayVal.value.size}") - asmgen.out(" jsr prog8_lib.containment_bytearray") - } - in WordDatatypes -> { - assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) - asmgen.out(" ldy #${arrayVal.value.size}") - asmgen.out(" jsr prog8_lib.containment_wordarray") - } - else -> throw AssemblyError("invalid dt") - } - return - } - else -> throw AssemblyError("invalid dt") - } + private fun containmentCheckIntoA(containment: PtContainmentCheck) { + val elementDt = containment.element.type + val symbol = asmgen.symbolTable.lookup(containment.iterable.name) + val variable = symbol!!.astNode as IPtVariable + val varname = asmgen.asmVariableName(containment.iterable) + val numElements = when(variable) { + is PtConstant -> null + is PtMemMapped -> variable.arraySize + is PtVariable -> variable.arraySize } - val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference) - when(variable.datatype) { + when(variable.type) { DataType.STR -> { // use subroutine - assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) - asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) + assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) + asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname) asmgen.restoreRegisterLocal(CpuRegister.A) - val stringVal = variable.value as StringLiteral + val stringVal = (variable as PtVariable).value as PtString asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" jsr prog8_lib.containment_bytearray") return @@ -874,19 +831,17 @@ internal class AssignmentAsmGen(private val program: Program, throw AssemblyError("containment check of floats not supported") } DataType.ARRAY_B, DataType.ARRAY_UB -> { - val numElements = variable.arraysize!!.constIndex()!! - assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) - asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) + assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) + asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname) asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_bytearray") return } DataType.ARRAY_W, DataType.ARRAY_UW -> { - val numElements = variable.arraysize!!.constIndex()!! - assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) + assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt, containment.definingISub()) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W2"), varname) asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_wordarray") return @@ -913,14 +868,13 @@ internal class AssignmentAsmGen(private val program: Program, assignRegisterByte(target, CpuRegister.A) } - private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) { - val valueIDt = value.inferType(program) - val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } + private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: PtExpression, origTypeCastExpression: PtTypeCast) { + val valueDt = value.type if(valueDt==targetDt) throw AssemblyError("type cast to identical dt should have been removed") when(value) { - is IdentifierReference -> { + is PtIdentifier -> { if(targetDt in WordDatatypes) { if(valueDt==DataType.UBYTE) { assignVariableUByteIntoWord(target, value) @@ -932,47 +886,47 @@ internal class AssignmentAsmGen(private val program: Program, } } } - is DirectMemoryRead -> { + is PtMemoryByte -> { if(targetDt in WordDatatypes) { - fun assignViaExprEval(addressExpression: Expression) { + fun assignViaExprEval(addressExpression: PtExpression) { asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") asmgen.out(" ldy #0") assignRegisterpairWord(target, RegisterOrPair.AY) } - when (value.addressExpression) { - is NumericLiteral -> { - val address = (value.addressExpression as NumericLiteral).number.toUInt() + when (value.address) { + is PtNumber -> { + val address = (value.address as PtNumber).number.toUInt() assignMemoryByteIntoWord(target, address, null) } - is IdentifierReference -> { - assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference) + is PtIdentifier -> { + assignMemoryByteIntoWord(target, null, value.address as PtIdentifier) } - is BinaryExpression -> { - if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) { + is PtBinaryExpression -> { + if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) { asmgen.out(" ldy #0") assignRegisterpairWord(target, RegisterOrPair.AY) } else { - assignViaExprEval(value.addressExpression) + assignViaExprEval(value.address) } } else -> { - assignViaExprEval(value.addressExpression) + assignViaExprEval(value.address) } } return } } - is NumericLiteral -> throw AssemblyError("a cast of a literal value should have been const-folded away") + is PtNumber -> throw AssemblyError("a cast of a literal value should have been const-folded away") else -> {} } // special case optimizations if(target.kind == TargetStorageKind.VARIABLE) { - if(value is IdentifierReference && valueDt != DataType.UNDEFINED) + if(value is PtIdentifier && valueDt != DataType.UNDEFINED) return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt) when (valueDt) { @@ -998,7 +952,7 @@ internal class AssignmentAsmGen(private val program: Program, } if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) { - val parentTc = origTypeCastExpression.parent as? TypecastExpression + val parentTc = origTypeCastExpression.parent as? PtTypeCast if(parentTc!=null && parentTc.type==DataType.UWORD) { // typecast a word value to ubyte and directly back to uword // generate code for lsb(value) here instead of the ubyte typecast @@ -1111,25 +1065,25 @@ internal class AssignmentAsmGen(private val program: Program, when(valueDt) { DataType.UBYTE -> { assignExpressionToRegister(value, RegisterOrPair.Y, false) - asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!) asmgen.out(" jsr floats.FREADUY") asmgen.restoreRegisterLocal(CpuRegister.X) } DataType.BYTE -> { assignExpressionToRegister(value, RegisterOrPair.A, true) - asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!) asmgen.out(" jsr floats.FREADSA") asmgen.restoreRegisterLocal(CpuRegister.X) } DataType.UWORD -> { assignExpressionToRegister(value, RegisterOrPair.AY, false) - asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!) asmgen.out(" jsr floats.GIVUAYFAY") asmgen.restoreRegisterLocal(CpuRegister.X) } DataType.WORD -> { assignExpressionToRegister(value, RegisterOrPair.AY, true) - asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) + asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!) asmgen.out(" jsr floats.GIVAYFAY") asmgen.restoreRegisterLocal(CpuRegister.X) } @@ -1147,9 +1101,10 @@ internal class AssignmentAsmGen(private val program: Program, asmgen.assignExpressionTo(origTypeCastExpression, target) } - private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) { - val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position) - lsb.linkParents(value.parent) + private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) { + val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position) + lsb.parent = value.parent + lsb.add(value) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val assign = AsmAssignment(src, target, false, program.memsizer, value.position) translateNormalAssignment(assign) @@ -1175,7 +1130,7 @@ internal class AssignmentAsmGen(private val program: Program, if(sourceDt == targetDt) throw AssemblyError("typecast to identical type") - // also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression) + // also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast) when(sourceDt) { DataType.UBYTE -> { when(targetDt) { @@ -1290,7 +1245,7 @@ internal class AssignmentAsmGen(private val program: Program, if(sourceDt == targetDt) throw AssemblyError("typecast to identical type") - // also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression) + // also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast) when(sourceDt) { DataType.UBYTE -> { when(targetDt) { @@ -1793,11 +1748,11 @@ internal class AssignmentAsmGen(private val program: Program, ldy #>${target.asmVarname} sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1""") - val constIndex = target.array!!.indexer.constIndex() + val constIndex = target.array!!.index.asConstInteger() if(constIndex!=null) { asmgen.out(" lda #$constIndex") } else { - val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) + val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier) asmgen.out(" lda $asmvarname") } asmgen.out(" jsr floats.set_array_float_from_fac1") @@ -1829,11 +1784,11 @@ internal class AssignmentAsmGen(private val program: Program, ldy #>${target.asmVarname} sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1""") - val constIndex = target.array!!.indexer.constIndex() + val constIndex = target.array!!.index.asConstInteger() if(constIndex!=null) { asmgen.out(" lda #$constIndex") } else { - val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) + val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier) asmgen.out(" lda $asmvarname") } asmgen.out(" jsr floats.set_array_float") @@ -1872,11 +1827,11 @@ internal class AssignmentAsmGen(private val program: Program, ldy #>${target.asmVarname} sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1""") - val constIndex = target.array!!.indexer.constIndex() + val constIndex = target.array!!.index.asConstInteger() if(constIndex!=null) { asmgen.out(" lda #$constIndex") } else { - val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) + val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier) asmgen.out(" lda $asmvarname") } asmgen.out(" jsr floats.set_array_float") @@ -1906,14 +1861,14 @@ internal class AssignmentAsmGen(private val program: Program, storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { - if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) { + if(target.origAstTarget?.array?.variable?.type==DataType.UWORD) { // assigning an indexed pointer var if (target.constArrayIndexValue==0u) { asmgen.out(" lda $sourceName") - asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) + asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable) } else { asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) - if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) { + if (asmgen.isZpVar(target.origAstTarget.array!!.variable)) { asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y") } else { asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") @@ -1961,7 +1916,7 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference) { + private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: PtIdentifier) { val sourceName = asmgen.asmVariableName(bytevar) when (wordtarget.kind) { TargetStorageKind.VARIABLE -> { @@ -2042,7 +1997,7 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun assignVariableUByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference) { + private fun assignVariableUByteIntoWord(wordtarget: AsmAssignTarget, bytevar: PtIdentifier) { val sourceName = asmgen.asmVariableName(bytevar) when(wordtarget.kind) { TargetStorageKind.VARIABLE -> { @@ -2129,7 +2084,7 @@ internal class AssignmentAsmGen(private val program: Program, CpuRegister.X -> asmgen.out(" txa") CpuRegister.Y -> asmgen.out(" tya") } - val indexVar = target.array!!.indexer.indexExpr as IdentifierReference + val indexVar = target.array!!.index as PtIdentifier asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y") } } @@ -2448,14 +2403,14 @@ internal class AssignmentAsmGen(private val program: Program, storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { - if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) { + if(target.origAstTarget?.array?.variable?.type==DataType.UWORD) { // assigning an indexed pointer var if (target.constArrayIndexValue==0u) { asmgen.out(" lda #0") - asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) + asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable) } else { asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) - if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) { + if (asmgen.isZpVar(target.origAstTarget.array!!.variable)) { asmgen.out(" lda #0 | sta (${target.asmVarname}),y") } else { asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") @@ -2504,14 +2459,14 @@ internal class AssignmentAsmGen(private val program: Program, storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { - if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) { + if(target.origAstTarget?.array?.variable?.type==DataType.UWORD) { // assigning an indexed pointer var if (target.constArrayIndexValue==0u) { asmgen.out(" lda #${byte.toHex()}") - asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) + asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable) } else { asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) - if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) { + if (asmgen.isZpVar(target.origAstTarget.array!!.variable)) { asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y") } else { asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") @@ -2585,7 +2540,7 @@ internal class AssignmentAsmGen(private val program: Program, """) } TargetStorageKind.ARRAY -> { - val constIndex = target.array!!.indexer.constIndex() + val constIndex = target.array!!.index.asConstInteger() if (constIndex!=null) { val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT) if(asmgen.isTargetCpu(CpuType.CPU65c02)) @@ -2606,7 +2561,7 @@ internal class AssignmentAsmGen(private val program: Program, sta ${target.asmVarname}+$indexValue+4 """) } else { - val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) + val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier) asmgen.out(""" lda #<${target.asmVarname} sta P8ZP_SCRATCH_W1 @@ -2647,7 +2602,7 @@ internal class AssignmentAsmGen(private val program: Program, } TargetStorageKind.ARRAY -> { val arrayVarName = target.asmVarname - val constIndex = target.array!!.indexer.constIndex() + val constIndex = target.array!!.index.asConstInteger() if (constIndex!=null) { val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT) asmgen.out(""" @@ -2659,7 +2614,7 @@ internal class AssignmentAsmGen(private val program: Program, ldy #>($arrayVarName+$indexValue) jsr floats.copy_float""") } else { - val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) + val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier) asmgen.out(""" lda #<${constFloat} sta P8ZP_SCRATCH_W1 @@ -2691,7 +2646,7 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun assignMemoryByte(target: AsmAssignTarget, address: UInt?, identifier: IdentifierReference?) { + private fun assignMemoryByte(target: AsmAssignTarget, address: UInt?, identifier: PtIdentifier?) { if (address != null) { when(target.kind) { TargetStorageKind.VARIABLE -> { @@ -2775,7 +2730,7 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: UInt?, identifier: IdentifierReference?) { + private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: UInt?, identifier: PtIdentifier?) { if (address != null) { when(wordtarget.kind) { TargetStorageKind.VARIABLE -> { @@ -2838,13 +2793,13 @@ internal class AssignmentAsmGen(private val program: Program, } } - private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) { - val addressExpr = memoryAddress.addressExpression - val addressLv = addressExpr as? NumericLiteral + private fun storeRegisterAInMemoryAddress(memoryAddress: PtMemoryByte) { + val addressExpr = memoryAddress.address + val addressLv = addressExpr as? PtNumber fun storeViaExprEval() { when(addressExpr) { - is NumericLiteral, is IdentifierReference -> { + is PtNumber, is PtIdentifier -> { assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2") } @@ -2862,10 +2817,10 @@ internal class AssignmentAsmGen(private val program: Program, addressLv != null -> { asmgen.out(" sta ${addressLv.number.toHex()}") } - addressExpr is IdentifierReference -> { + addressExpr is PtIdentifier -> { asmgen.storeAIntoPointerVar(addressExpr) } - addressExpr is BinaryExpression -> { + addressExpr is PtBinaryExpression -> { if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true)) storeViaExprEval() } @@ -2873,15 +2828,15 @@ internal class AssignmentAsmGen(private val program: Program, } } - internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean) { + internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) { val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) translateNormalAssignment(assign) } - internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) { - if(expr.inferType(program) istype DataType.FLOAT && dt!=DataType.FLOAT) { + internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) { + if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) { throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr") } else { val src = AsmAssignSource.fromAstSource(expr, program, asmgen) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index e185bf326..29aedf238 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -1,17 +1,14 @@ package prog8.codegen.cpu6502.assignment -import prog8.ast.Program -import prog8.ast.base.FatalAstException -import prog8.ast.expressions.* -import prog8.ast.statements.Subroutine +import prog8.code.ast.* import prog8.code.core.* -import prog8.codegen.cpu6502.AsmGen +import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.VariableAllocator -internal class AugmentableAssignmentAsmGen(private val program: Program, +internal class AugmentableAssignmentAsmGen(private val program: PtProgram, private val assignmentAsmGen: AssignmentAsmGen, - private val asmgen: AsmGen, + private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator ) { fun translate(assign: AsmAssignment) { @@ -21,7 +18,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } when (val value = assign.source.expression!!) { - is PrefixExpression -> { + is PtPrefix -> { // A = -A , A = +A, A = ~A, A = not A when (value.operator) { "+" -> {} @@ -30,13 +27,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, else -> throw AssemblyError("invalid prefix operator") } } - is TypecastExpression -> inplaceCast(assign.target, value, assign.position) - is BinaryExpression -> inplaceBinary(assign.target, value) + is PtTypeCast -> inplaceCast(assign.target, value, assign.position) + is PtBinaryExpression -> inplaceBinary(assign.target, value) else -> throw AssemblyError("invalid aug assign value type") } } - private fun inplaceBinary(target: AsmAssignTarget, binExpr: BinaryExpression) { + private fun inplaceBinary(target: AsmAssignTarget, binExpr: PtBinaryExpression) { val astTarget = target.origAstTarget!! if (binExpr.left isSameAs astTarget) { // A = A Something @@ -49,7 +46,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, return inplaceModification(target, binExpr.operator, binExpr.left) } - val leftBinExpr = binExpr.left as? BinaryExpression + val leftBinExpr = binExpr.left as? PtBinaryExpression if (leftBinExpr?.operator == binExpr.operator) { // TODO better optimize the chained asm to avoid intermediate stores/loads? when { @@ -73,7 +70,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } } - val rightBinExpr = binExpr.right as? BinaryExpression + val rightBinExpr = binExpr.right as? PtBinaryExpression if (rightBinExpr?.operator == binExpr.operator) { when { binExpr.left isSameAs astTarget -> { @@ -98,8 +95,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - val leftBinExpr = binExpr.left as? BinaryExpression - val rightBinExpr = binExpr.right as? BinaryExpression + val leftBinExpr = binExpr.left as? PtBinaryExpression + val rightBinExpr = binExpr.right as? PtBinaryExpression if(leftBinExpr!=null && rightBinExpr==null) { if(leftBinExpr.left isSameAs astTarget) { // X = (X Right) Something @@ -141,26 +138,27 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - throw FatalAstException("assignment should follow augmentable rules $binExpr") + throw AssemblyError("assignment should follow augmentable rules $binExpr") } - private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: Expression) { + private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: PtExpression) { // the asm-gen code can deal with situations where you want to assign a byte into a word. // it will create the most optimized code to do this (so it type-extends for us). // But we can't deal with writing a word into a byte - explicit typeconversion is required - val value = if(program.memsizer.memorySize(origValue.inferType(program).getOrElse { throw AssemblyError("unknown dt") }) > program.memsizer.memorySize(target.datatype)) { - val typecast = TypecastExpression(origValue, target.datatype, true, origValue.position) - typecast.linkParents(origValue.parent) + val value = if(program.memsizer.memorySize(origValue.type) > program.memsizer.memorySize(target.datatype)) { + val typecast = PtTypeCast(target.datatype, origValue.position) + typecast.add(origValue) + require(typecast.type!=origValue.type) typecast } else { origValue } - val valueLv = (value as? NumericLiteral)?.number - val ident = value as? IdentifierReference - val memread = value as? DirectMemoryRead + val valueLv = (value as? PtNumber)?.number + val ident = value as? PtIdentifier + val memread = value as? PtMemoryByte when (target.kind) { TargetStorageKind.VARIABLE -> { @@ -170,7 +168,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident) memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value) } @@ -182,7 +180,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident) memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value) @@ -194,7 +192,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when { valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!) ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!) } @@ -206,27 +204,27 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.MEMORY -> { val memory = target.memory!! - when (memory.addressExpression) { - is NumericLiteral -> { - val addr = (memory.addressExpression as NumericLiteral).number.toInt() + when (memory.address) { + is PtNumber -> { + val addr = (memory.address as PtNumber).number.toInt() // re-use code to assign a variable, instead this time, use a direct memory address when { valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt()) ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident) memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value) } else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value) } } - is IdentifierReference -> { - val pointer = memory.addressExpression as IdentifierReference + is PtIdentifier -> { + val pointer = memory.address as PtIdentifier when { valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt()) ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_byte_value_to_pointer(pointer, operator, value) } @@ -235,13 +233,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } else -> { // TODO use some other evaluation here; don't use the estack to transfer the address to read/write from - asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingSubroutine)) + asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingISub())) asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") when { valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt()) ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident) memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread) - value is TypecastExpression -> { + value is PtTypeCast -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value) } @@ -252,91 +250,89 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } TargetStorageKind.ARRAY -> { - with(target.array!!.indexer) { - val indexNum = indexExpr as? NumericLiteral - val indexVar = indexExpr as? IdentifierReference - when { - indexNum!=null -> { - val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}" - when (target.datatype) { - in ByteDatatypes -> { - when { - valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt()) - ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident) - memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread) - value is TypecastExpression -> { - if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return - inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value) - } - else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value) + val indexNum = target.array!!.index as? PtNumber + val indexVar = target.array.index as? PtIdentifier + when { + indexNum!=null -> { + val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}" + when (target.datatype) { + in ByteDatatypes -> { + when { + valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt()) + ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident) + memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread) + value is PtTypeCast -> { + if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return + inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value) } + else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value) } - in WordDatatypes -> { - when { - valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt()) - ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident) - memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread) - value is TypecastExpression -> { - if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return - inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value) - } - else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value) - } - } - DataType.FLOAT -> { - when { - valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!) - ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!) - value is TypecastExpression -> { - if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return - inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!) - } - else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!) - } - } - else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") } - } - indexVar!=null -> { - when (target.datatype) { - in ByteDatatypes -> { - val tgt = - AsmAssignTarget.fromRegisters( - RegisterOrPair.A, - target.datatype == DataType.BYTE, null, - asmgen - ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) - assignmentAsmGen.translateNormalAssignment(assign) - assignmentAsmGen.assignRegisterByte(target, CpuRegister.A) + in WordDatatypes -> { + when { + valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt()) + ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident) + memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread) + value is PtTypeCast -> { + if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return + inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value) + } + else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value) } - in WordDatatypes -> { - val tgt = - AsmAssignTarget.fromRegisters( - RegisterOrPair.AY, - target.datatype == DataType.WORD, null, - asmgen - ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) - assignmentAsmGen.translateNormalAssignment(assign) - assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) - } - DataType.FLOAT -> { - val tgt = - AsmAssignTarget.fromRegisters( - RegisterOrPair.FAC1, - true, null, - asmgen - ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) - assignmentAsmGen.translateNormalAssignment(assign) - assignmentAsmGen.assignFAC1float(target) - } - else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") } + DataType.FLOAT -> { + when { + valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!) + ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!) + value is PtTypeCast -> { + if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return + inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!) + } + else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!) + } + } + else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") } - else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var") } + indexVar!=null -> { + when (target.datatype) { + in ByteDatatypes -> { + val tgt = + AsmAssignTarget.fromRegisters( + RegisterOrPair.A, + target.datatype == DataType.BYTE, null, + asmgen + ) + val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + assignmentAsmGen.translateNormalAssignment(assign) + assignmentAsmGen.assignRegisterByte(target, CpuRegister.A) + } + in WordDatatypes -> { + val tgt = + AsmAssignTarget.fromRegisters( + RegisterOrPair.AY, + target.datatype == DataType.WORD, null, + asmgen + ) + val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + assignmentAsmGen.translateNormalAssignment(assign) + assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) + } + DataType.FLOAT -> { + val tgt = + AsmAssignTarget.fromRegisters( + RegisterOrPair.FAC1, + true, null, + asmgen + ) + val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + assignmentAsmGen.translateNormalAssignment(assign) + assignmentAsmGen.assignFAC1float(target) + } + else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") + } + } + else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var") } } TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification") @@ -344,21 +340,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun tryInplaceModifyWithRemovedRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean { + private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean { if (target.datatype == value.type) { - val childIDt = value.expression.inferType(program) - val childDt = childIDt.getOrElse { throw AssemblyError("unknown dt") } + val childDt = value.value.type if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) { // this typecast is redundant here; the rest of the code knows how to deal with the uncasted value. // (works for integer types, not for float.) - inplaceModification(target, operator, value.expression) + inplaceModification(target, operator, value.value) return true } } return false } - private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) { + private fun inplaceModification_byte_value_to_pointer(pointervar: PtIdentifier, operator: String, value: PtExpression) { asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) when (operator) { @@ -394,7 +389,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.storeAIntoZpPointerVar(sourceName) } - private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) { + private fun inplaceModification_byte_variable_to_pointer(pointervar: PtIdentifier, operator: String, value: PtIdentifier) { val otherName = asmgen.asmVariableName(value) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) @@ -431,7 +426,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.storeAIntoZpPointerVar(sourceName) } - private fun inplaceModification_byte_litval_to_pointer(pointervar: IdentifierReference, operator: String, value: Int) { + private fun inplaceModification_byte_litval_to_pointer(pointervar: PtIdentifier, operator: String, value: Int) { when (operator) { // note: ** (power) operator requires floats. "+" -> { @@ -499,7 +494,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { + private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) { // this should be the last resort for code generation for this, // because the value is evaluated onto the eval stack (=slow). when (operator) { @@ -594,7 +589,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { + private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: PtIdentifier) { val otherName = asmgen.asmVariableName(ident) when (operator) { // note: ** (power) operator requires floats. @@ -762,7 +757,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) { + private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) { when (operator) { "+" -> { asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) @@ -799,7 +794,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) { + private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) { when (operator) { "+" -> { asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) @@ -1111,9 +1106,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { + private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: PtIdentifier) { val otherName = asmgen.asmVariableName(ident) - when (val valueDt = ident.targetVarDecl(program)!!.datatype) { + when (val valueDt = ident.type) { in ByteDatatypes -> { // the other variable is a BYTE type so optimize for that when (operator) { @@ -1356,12 +1351,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { + private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) { // this should be the last resort for code generation for this, // because the value is evaluated onto the eval stack (=slow). - val valueiDt = value.inferType(program) - val valueDt = valueiDt.getOrElse { throw AssemblyError("unknown dt") } fun multiplyVarByWordInAY() { asmgen.out(""" @@ -1410,7 +1403,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, """) } - when (valueDt) { + when (val valueDt = value.type) { in ByteDatatypes -> { // the other variable is a BYTE type so optimize for that when (operator) { @@ -1581,7 +1574,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) { + private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: IPtSubroutine) { asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1) asmgen.saveRegisterLocal(CpuRegister.X, scope) when (operator) { @@ -1623,8 +1616,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.restoreRegisterLocal(CpuRegister.X) } - private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) { - val valueDt = ident.targetVarDecl(program)!!.datatype + private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: PtIdentifier, scope: IPtSubroutine) { + val valueDt = ident.type if(valueDt != DataType.FLOAT) throw AssemblyError("float variable expected") @@ -1682,7 +1675,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.restoreRegisterLocal(CpuRegister.X) } - private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) { + private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: IPtSubroutine) { val constValueName = allocator.getFloatAsmConst(value) asmgen.saveRegisterLocal(CpuRegister.X, scope) when (operator) { @@ -1744,9 +1737,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.restoreRegisterLocal(CpuRegister.X) } - private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) { + private fun inplaceCast(target: AsmAssignTarget, cast: PtTypeCast, position: Position) { val outerCastDt = cast.type - val innerCastDt = (cast.expression as? TypecastExpression)?.type + val innerCastDt = (cast.value as? PtTypeCast)?.type if (innerCastDt == null) { // simple typecast where the value is the target when (target.datatype) { @@ -1789,8 +1782,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // typecast with nested typecast, that has the target as a value // calculate singular cast that is required val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt - val value = (cast.expression as TypecastExpression).expression - val resultingCast = TypecastExpression(value, castDt, false, position) + val resultingCast = PtTypeCast(castDt, position) + resultingCast.add((cast.value as PtTypeCast).value) + require(castDt!=resultingCast.value.type) inplaceCast(target, resultingCast, position) } } @@ -1808,21 +1802,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.MEMORY -> { val memory = target.memory!! - when (memory.addressExpression) { - is NumericLiteral -> { - val addr = (memory.addressExpression as NumericLiteral).number.toHex() + when (memory.address) { + is PtNumber -> { + val addr = (memory.address as PtNumber).number.toHex() asmgen.out(""" lda $addr eor #255 sta $addr""") } - is IdentifierReference -> { - val sourceName = asmgen.loadByteFromPointerIntoA(memory.addressExpression as IdentifierReference) + is PtIdentifier -> { + val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier) asmgen.out(" eor #255") asmgen.out(" sta ($sourceName),y") } else -> { - asmgen.assignExpressionToVariable(memory.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) + asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) asmgen.out(""" ldy #0 lda (P8ZP_SCRATCH_W2),y diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt new file mode 100644 index 000000000..f2c447632 --- /dev/null +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -0,0 +1,10 @@ +package prog8tests.codegencpu6502 + +import io.kotest.core.spec.style.FunSpec + +class TestCodegen: FunSpec({ + + // TODO there are no 6502 specific codegen tests yet + +}) + diff --git a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt similarity index 71% rename from codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt rename to codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt index b105dc8e3..617f2f50b 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt @@ -3,19 +3,19 @@ package prog8.codegen.experimental import prog8.code.SymbolTable import prog8.code.ast.PtProgram import prog8.code.core.CompilationOptions -import prog8.code.core.IAssemblyGenerator import prog8.code.core.IAssemblyProgram +import prog8.code.core.ICodeGeneratorBackend import prog8.code.core.IErrorReporter import prog8.codegen.intermediate.IRCodeGen import prog8.intermediate.IRFileWriter -class CodeGen(private val program: PtProgram, - private val symbolTable: SymbolTable, - private val options: CompilationOptions, - private val errors: IErrorReporter -): IAssemblyGenerator { - override fun compileToAssembly(): IAssemblyProgram? { - +class ExperiCodeGen: ICodeGeneratorBackend { + override fun generate( + program: PtProgram, + symbolTable: SymbolTable, + options: CompilationOptions, + errors: IErrorReporter + ): IAssemblyProgram? { // you could write a code generator directly on the PtProgram AST, // but you can also use the Intermediate Representation to build a codegen on: val irCodeGen = IRCodeGen(program, symbolTable, options, errors) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index a93a39b5e..05fe03c67 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -988,20 +988,20 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { if(fcall.type==DataType.FLOAT) throw AssemblyError("doesn't support float register result in asm romsub") val returns = callTarget.returns.single() - val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString() + val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString() addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null) } else -> { - val returnRegister = callTarget.returns.singleOrNull{ it.registerOrPair!=null } + val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null } if(returnRegister!=null) { // we skip the other values returned in the status flags. - val regStr = returnRegister.registerOrPair.toString() + val regStr = returnRegister.register.registerOrPair.toString() addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null) } else { - val firstReturnRegister = callTarget.returns.firstOrNull{ it.registerOrPair!=null } + val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null } if(firstReturnRegister!=null) { // we just take the first register return value and ignore the rest. - val regStr = firstReturnRegister.registerOrPair.toString() + val regStr = firstReturnRegister.register.registerOrPair.toString() addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null) } else { throw AssemblyError("invalid number of return values from call") diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 2af57ae31..31a0bed03 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1203,8 +1203,8 @@ class IRCodeGen( child.name, child.address, child.clobbers, - child.parameters.map { IRAsmSubroutine.IRAsmParam(it.second, it.first.type) }, // note: the name of the asmsub param is not used anymore. - child.returnTypes.zip(child.retvalRegisters).map { IRAsmSubroutine.IRAsmParam(it.second, it.first) }, + child.parameters.map { IRAsmSubroutine.IRAsmParam(it.first, it.second.type) }, // note: the name of the asmsub param is not used here anymore + child.returns.map { IRAsmSubroutine.IRAsmParam(it.first, it.second)}, asmChunk, child.position ) diff --git a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt index 6035ef9a3..0576b15f6 100644 --- a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt @@ -3,23 +3,22 @@ package prog8.codegen.vm import prog8.code.SymbolTable import prog8.code.ast.PtProgram import prog8.code.core.CompilationOptions -import prog8.code.core.IAssemblyGenerator import prog8.code.core.IAssemblyProgram +import prog8.code.core.ICodeGeneratorBackend import prog8.code.core.IErrorReporter import prog8.codegen.intermediate.IRCodeGen import prog8.intermediate.IRFileWriter import prog8.intermediate.IRProgram -class VmCodeGen(private val program: PtProgram, - private val symbolTable: SymbolTable, - private val options: CompilationOptions, - private val errors: IErrorReporter -): IAssemblyGenerator { - override fun compileToAssembly(): IAssemblyProgram? { - +class VmCodeGen: ICodeGeneratorBackend { + override fun generate( + program: PtProgram, + symbolTable: SymbolTable, + options: CompilationOptions, + errors: IErrorReporter + ): IAssemblyProgram? { val irCodeGen = IRCodeGen(program, symbolTable, options, errors) val irProgram = irCodeGen.generate() - return VmAssemblyProgram(irProgram.name, irProgram) } } diff --git a/codeGenIntermediate/test/Dummies.kt b/codeGenIntermediate/test/Dummies.kt index 7b51303bd..bfa03f50b 100644 --- a/codeGenIntermediate/test/Dummies.kt +++ b/codeGenIntermediate/test/Dummies.kt @@ -14,7 +14,7 @@ internal object DummyStringEncoder : IStringEncoding { return emptyList() } - override fun decodeString(bytes: List, encoding: Encoding): String { + override fun decodeString(bytes: Iterable, encoding: Encoding): String { return "" } } diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index bd633a418..dd232285a 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -206,13 +206,13 @@ private fun compileMain(args: Array): Boolean { } if(startEmulator1==true || startEmulator2==true) { - if (compilationResult.program.name.isEmpty()) { + if (compilationResult.compilerAst.name.isEmpty()) { println("\nCan't start emulator because no program was assembled.") return true } } - val programNameInPath = outputPath.resolve(compilationResult.program.name) + val programNameInPath = outputPath.resolve(compilationResult.compilerAst.name) if(startEmulator1==true || startEmulator2==true) { if (compilationResult.compilationOptions.launcher != CbmPrgLauncherType.NONE || compilationTarget=="atari") { diff --git a/compiler/src/prog8/compiler/BuiltinFunctions.kt b/compiler/src/prog8/compiler/BuiltinFunctions.kt new file mode 100644 index 000000000..1c80b7889 --- /dev/null +++ b/compiler/src/prog8/compiler/BuiltinFunctions.kt @@ -0,0 +1,158 @@ +package prog8.compiler + +import prog8.ast.Program +import prog8.ast.base.AstException +import prog8.ast.base.FatalAstException +import prog8.ast.base.SyntaxError +import prog8.ast.expressions.* +import prog8.ast.statements.VarDecl +import prog8.code.core.* +import kotlin.math.abs +import kotlin.math.sign +import kotlin.math.sqrt + + +private typealias ConstExpressionCaller = (args: List, position: Position, program: Program) -> NumericLiteral + +internal val constEvaluatorsForBuiltinFuncs: Map = mapOf( + "abs" to ::builtinAbs, + "len" to ::builtinLen, + "sizeof" to ::builtinSizeof, + "sgn" to ::builtinSgn, + "sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, + "any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, + "all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, + "lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, + "msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, + "mkword" to ::builtinMkword +) + +private fun builtinAny(array: List): Double = if(array.any { it!=0.0 }) 1.0 else 0.0 + +private fun builtinAll(array: List): Double = if(array.all { it!=0.0 }) 1.0 else 0.0 + +internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType { + if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd")) + return InferredTypes.InferredType.void() + + val func = BuiltinFunctions.getValue(function) + val returnType = func.returnType + return if(returnType==null) + InferredTypes.InferredType.void() + else + InferredTypes.knownFor(returnType) +} + + +internal class NotConstArgumentException: AstException("not a const argument to a built-in function") +internal class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg") + + +private fun oneIntArgOutputInt(args: List, position: Position, program: Program, function: (arg: Int)->Double): NumericLiteral { + if(args.size!=1) + throw SyntaxError("built-in function requires one integer argument", position) + val constval = args[0].constValue(program) ?: throw NotConstArgumentException() + if(constval.type != DataType.UBYTE && constval.type!= DataType.UWORD) + throw SyntaxError("built-in function requires one integer argument", position) + + val integer = constval.number.toInt() + return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position) +} + +private fun collectionArg(args: List, position: Position, program: Program, function: (arg: List)->Double): NumericLiteral { + if(args.size!=1) + throw SyntaxError("builtin function requires one non-scalar argument", position) + + val array= args[0] as? ArrayLiteral ?: throw NotConstArgumentException() + val constElements = array.value.map{it.constValue(program)?.number} + if(constElements.contains(null)) + throw NotConstArgumentException() + + return NumericLiteral.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position) +} + +private fun builtinAbs(args: List, position: Position, program: Program): NumericLiteral { + // 1 arg, type = int, result type= uword + if(args.size!=1) + throw SyntaxError("abs requires one integer argument", position) + + val constval = args[0].constValue(program) ?: throw NotConstArgumentException() + return when (constval.type) { + in IntegerDatatypesNoBool -> NumericLiteral.optimalInteger(abs(constval.number.toInt()), args[0].position) + else -> throw SyntaxError("abs requires one integer argument", position) + } +} + +private fun builtinSizeof(args: List, position: Position, program: Program): NumericLiteral { + // 1 arg, type = anything, result type = ubyte + if(args.size!=1) + throw SyntaxError("sizeof requires one argument", position) + if(args[0] !is IdentifierReference) + throw SyntaxError("sizeof argument should be an identifier", position) + + val dt = args[0].inferType(program) + if(dt.isKnown) { + val target = (args[0] as IdentifierReference).targetStatement(program) + ?: throw CannotEvaluateException("sizeof", "no target") + + return when { + dt.isArray -> { + val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size") + val elementDt = ArrayToElementTypes.getValue(dt.getOr(DataType.UNDEFINED)) + NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt) * length, position) + } + dt istype DataType.STR -> throw SyntaxError("sizeof str is undefined, did you mean len?", position) + else -> NumericLiteral(DataType.UBYTE, program.memsizer.memorySize(dt.getOr(DataType.UNDEFINED)).toDouble(), position) + } + } else { + throw SyntaxError("sizeof invalid argument type", position) + } +} + +private fun builtinLen(args: List, position: Position, program: Program): NumericLiteral { + // note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE. + if(args.size!=1) + throw SyntaxError("len requires one argument", position) + + val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program) + var arraySize = directMemVar?.arraysize?.constIndex() + if(arraySize != null) + return NumericLiteral.optimalInteger(arraySize, position) + if(args[0] is ArrayLiteral) + return NumericLiteral.optimalInteger((args[0] as ArrayLiteral).value.size, position) + if(args[0] !is IdentifierReference) + throw SyntaxError("len argument should be an identifier", position) + val target = (args[0] as IdentifierReference).targetVarDecl(program) + ?: throw CannotEvaluateException("len", "no target vardecl") + + return when(target.datatype) { + in ArrayDatatypes -> { + arraySize = target.arraysize?.constIndex() + if(arraySize==null) + throw CannotEvaluateException("len", "arraysize unknown") + NumericLiteral.optimalInteger(arraySize, args[0].position) + } + DataType.STR -> { + val refLv = target.value as? StringLiteral ?: throw CannotEvaluateException("len", "stringsize unknown") + NumericLiteral.optimalInteger(refLv.value.length, args[0].position) + } + in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position) + else -> throw InternalCompilerException("weird datatype") + } +} + +private fun builtinMkword(args: List, position: Position, program: Program): NumericLiteral { + if (args.size != 2) + throw SyntaxError("mkword requires msb and lsb arguments", position) + val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException() + val constLsb = args[1].constValue(program) ?: throw NotConstArgumentException() + val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt() + return NumericLiteral(DataType.UWORD, result.toDouble(), position) +} + +private fun builtinSgn(args: List, position: Position, program: Program): NumericLiteral { + if (args.size != 1) + throw SyntaxError("sgn requires one argument", position) + val constval = args[0].constValue(program) ?: throw NotConstArgumentException() + return NumericLiteral(DataType.BYTE, constval.number.sign, position) +} diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 2e7769e16..f21634278 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1,17 +1,14 @@ package prog8.compiler import com.github.michaelbull.result.onFailure -import prog8.ast.AstToSourceTextConverter import prog8.ast.IBuiltinFunctions -import prog8.ast.IStatementContainer import prog8.ast.Program import prog8.ast.base.AstException import prog8.ast.expressions.Expression import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.Directive -import prog8.ast.statements.VarDecl -import prog8.ast.walk.IAstVisitor -import prog8.code.SymbolTable +import prog8.code.SymbolTableMaker +import prog8.code.ast.PtProgram import prog8.code.core.* import prog8.code.target.* import prog8.codegen.vm.VmCodeGen @@ -25,7 +22,8 @@ import kotlin.math.round import kotlin.system.measureTimeMillis -class CompilationResult(val program: Program, +class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead + val codegenAst: PtProgram?, val compilationOptions: CompilationOptions, val importedFiles: List) @@ -63,6 +61,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { } var compilationOptions: CompilationOptions + var ast: PtProgram? = null try { val totalTime = measureTimeMillis { @@ -113,10 +112,22 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { args.errors.report() if (args.writeAssembly) { - if(!createAssemblyAndAssemble(program, args.errors, compilationOptions)) { + + compilationOptions.compTarget.machine.initializeMemoryAreas(compilationOptions) + program.processAstBeforeAsmGeneration(compilationOptions, args.errors) + args.errors.report() + + val intermediateAst = IntermediateAstMaker(program, compilationOptions).transform() +// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************") +// printProgram(program) +// println("*********** AST RIGHT BEFORE ASM GENERATION *************") +// printAst(intermediateAst, ::println) + + if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) { System.err.println("Error in codegeneration or assembler") return null } + ast = intermediateAst } } @@ -124,7 +135,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { System.err.flush() val seconds = totalTime/1000.0 println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.") - return CompilationResult(program, compilationOptions, importedFiles) + return CompilationResult(program, ast, compilationOptions, importedFiles) } catch (px: ParseError) { System.err.print("\n\u001b[91m") // bright red System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim()) @@ -208,7 +219,7 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt override fun constValue(funcName: String, args: List, position: Position): NumericLiteral? { val func = BuiltinFunctions[funcName] if(func!=null) { - val exprfunc = func.constExpressionFunc + val exprfunc = constEvaluatorsForBuiltinFuncs[funcName] if(exprfunc!=null) { return try { exprfunc(args, position, program) @@ -385,25 +396,23 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt errors.report() } -private fun createAssemblyAndAssemble(program: Program, +private fun createAssemblyAndAssemble(program: PtProgram, errors: IErrorReporter, compilerOptions: CompilationOptions ): Boolean { - compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions) - program.processAstBeforeAsmGeneration(compilerOptions, errors) - errors.report() - val symbolTable = SymbolTableMaker(program, compilerOptions).make() - // TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast, - // or don't use inferType at all anymore and "bake the type information" into the Ast somehow. - // Note: we don't actually *need* to remove the VarDecl nodes, but it is nice as a temporary measure - // to help clean up the code that still depends on them. - // removeAllVardeclsFromAst(program) + val asmgen = if(compilerOptions.experimentalCodegen) + prog8.codegen.experimental.ExperiCodeGen() + else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) + prog8.codegen.cpu6502.AsmGen6502() + else if (compilerOptions.compTarget.name == VMTarget.NAME) + VmCodeGen() + else + throw NotImplementedError("no asm generator for cpu ${compilerOptions.compTarget.machine.cpu}") -// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************") -// printProgram(program) - - val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly() + val stMaker = SymbolTableMaker(program, compilerOptions) + val symbolTable = stMaker.make() + val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors) errors.report() return if(assembly!=null && errors.noErrors()) { @@ -412,51 +421,3 @@ private fun createAssemblyAndAssemble(program: Program, false } } - -private fun removeAllVardeclsFromAst(program: Program) { - // remove all VarDecl nodes from the AST. - // code generation doesn't require them anymore, it operates only on the 'variables' collection. - - class SearchAndRemove: IAstVisitor { - private val allVars = mutableListOf() - init { - visit(program) - for (it in allVars) { - require((it.parent as IStatementContainer).statements.remove(it)) - } - } - override fun visit(decl: VarDecl) { - allVars.add(decl) - } - } - - SearchAndRemove() -} - -fun printProgram(program: Program) { - println() - val printer = AstToSourceTextConverter(::print, program) - printer.visit(program) - println() -} - -internal fun asmGeneratorFor(program: Program, - errors: IErrorReporter, - symbolTable: SymbolTable, - options: CompilationOptions): IAssemblyGenerator -{ - if(options.experimentalCodegen) { - val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform() - return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors) - } else { - if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) - // TODO rewrite 6502 codegen on new Intermediary Ast or on new Intermediate Representation - return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors) - if (options.compTarget.name == VMTarget.NAME) { - val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform() - return VmCodeGen(intermediateAst, symbolTable, options, errors) - } - } - - throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}") -} diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index d9aa532a2..ae85997d9 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -8,8 +8,6 @@ import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor import prog8.code.core.* import prog8.code.target.VMTarget -import prog8.compiler.BuiltinFunctions -import prog8.compiler.InplaceModifyingBuiltinFunctions import prog8.compiler.builtinFunctionReturnType import java.io.CharConversionException import java.io.File @@ -62,10 +60,12 @@ internal class AstChecker(private val program: Program, } override fun visit(identifier: IdentifierReference) { - val targetParam = identifier.targetVarDecl(program)?.subroutineParameter - if(targetParam!=null) { - if((targetParam.parent as Subroutine).isAsmSubroutine) - errors.err("cannot refer to parameter of asmsub by name", identifier.position) + val target = identifier.targetVarDecl(program) + if(target != null && target.origin==VarDeclOrigin.SUBROUTINEPARAM) { + if(target.definingSubroutine!!.isAsmSubroutine) { + if(target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() }) + errors.err("cannot refer to parameter of asmsub by name", identifier.position) + } } } @@ -664,8 +664,12 @@ internal class AstChecker(private val program: Program, err("string var must be initialized with a string literal") } - if(decl.value !is StringLiteral) - err("string var must be initialized with a string literal") + if(decl.value !is StringLiteral) { + if(decl.type==VarDeclType.MEMORY) + err("strings can't be memory mapped") + else + err("string var must be initialized with a string literal") + } } if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE) diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 7446825a6..c26afced3 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -136,12 +136,8 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) { val process = VariousCleanups(this, errors, options) process.visit(this) - if(errors.noErrors()) { - if(process.applyModifications()>0) { - process.visit(this) - if(errors.noErrors()) - process.applyModifications() - } + while(errors.noErrors() && process.applyModifications()>0) { + process.visit(this) } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index bf1254f1e..2580a9146 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -7,11 +7,11 @@ import prog8.ast.expressions.FunctionCallExpression import prog8.ast.expressions.StringLiteral import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor +import prog8.code.core.BuiltinFunctions import prog8.code.core.ICompilationTarget import prog8.code.core.IErrorReporter import prog8.code.core.Position import prog8.code.target.VMTarget -import prog8.compiler.BuiltinFunctions internal class AstIdentifiersChecker(private val errors: IErrorReporter, @@ -92,7 +92,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, val paramsToCheck = paramNames.intersect(namesInSub) for(name in paramsToCheck) { val symbol = subroutine.searchSymbol(name) - if(symbol!=null && (symbol as? VarDecl)?.subroutineParameter==null) + if(symbol!=null && (symbol as? VarDecl)?.origin!=VarDeclOrigin.SUBROUTINEPARAM) nameError(name, symbol.position, subroutine) } @@ -163,7 +163,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, val pos = (if(call.args.any()) call.args[0] else (call as Node)).position errors.err("invalid number of arguments", pos) } - if(func.name=="memory") { + if(target.name=="memory") { val name = call.args[0] as? StringLiteral if(name!=null) { val processed = name.value.map { diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index a5dc977b6..19dc057a2 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -90,7 +90,7 @@ internal class BeforeAsmAstChanger(val program: Program, "unknown dt" ) }, sourceDt, implicit=true) - val assignRight = Assignment(assignment.target, right, AssignmentOrigin.BEFOREASMGEN, assignment.position) + val assignRight = Assignment(assignment.target, right, AssignmentOrigin.ASMGEN, assignment.position) return listOf( IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer), IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr), @@ -103,7 +103,7 @@ internal class BeforeAsmAstChanger(val program: Program, "unknown dt" ) }, sourceDt, implicit=true) - val assignLeft = Assignment(assignment.target, left, AssignmentOrigin.BEFOREASMGEN, assignment.position) + val assignLeft = Assignment(assignment.target, left, AssignmentOrigin.ASMGEN, assignment.position) return listOf( IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer), IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr) @@ -293,7 +293,7 @@ internal class BeforeAsmAstChanger(val program: Program, val dt = expr.indexer.indexExpr.inferType(program) val (tempVarName, _) = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") }) val target = AssignTarget(IdentifierReference(tempVarName, expr.indexer.position), null, null, expr.indexer.position) - val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.BEFOREASMGEN, expr.indexer.position) + val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.ASMGEN, expr.indexer.position) modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer)) modifications.add( IAstModification.ReplaceNode( diff --git a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt index a85ca8019..4b96c8c3e 100644 --- a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt +++ b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt @@ -30,7 +30,7 @@ internal class BoolRemover(val program: Program) : AstWalker() { newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position) } val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name, - newvalue, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position) + newvalue, decl.isArray, decl.sharedWithAsm, decl.position) return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent)) } @@ -47,7 +47,7 @@ internal class BoolRemover(val program: Program) : AstWalker() { newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position) } val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name, - newarray, true, decl.sharedWithAsm, decl.subroutineParameter, decl.position) + newarray, true, decl.sharedWithAsm, decl.position) return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent)) } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index d9ba3bb58..d610667a1 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -8,12 +8,11 @@ import prog8.ast.Program import prog8.ast.base.FatalAstException import prog8.ast.expressions.* import prog8.ast.statements.* -import prog8.code.SymbolTable import prog8.code.ast.* +import prog8.code.core.BuiltinFunctions import prog8.code.core.CompilationOptions import prog8.code.core.DataType import prog8.code.core.SourceCode -import prog8.compiler.BuiltinFunctions import prog8.compiler.builtinFunctionReturnType import java.io.File import kotlin.io.path.Path @@ -23,7 +22,7 @@ import kotlin.io.path.isRegularFile /** * Convert 'old' compiler-AST into the 'new' simplified AST with baked types. */ -class IntermediateAstMaker(private val program: Program, private val symbolTable: SymbolTable, private val options: CompilationOptions) { +class IntermediateAstMaker(private val program: Program, private val options: CompilationOptions) { fun transform(): PtProgram { val ptProgram = PtProgram( program.name, @@ -130,7 +129,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable } } val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl } - val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, alignment, srcBlock.position) + val src = srcBlock.definingModule.source + val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, alignment, src, srcBlock.position) makeScopeVarsDecls(vardecls).forEach { block.add(it) } for (stmt in statements) block.add(transformStatement(stmt)) @@ -285,18 +285,15 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable } private fun transformAsmSub(srcSub: Subroutine): PtAsmSub { - val params = srcSub.parameters - .map { PtSubroutineParameter(it.name, it.type, it.position) } - .zip(srcSub.asmParameterRegisters) + val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) }) val sub = PtAsmSub(srcSub.name, srcSub.asmAddress, srcSub.asmClobbers, params, - srcSub.returntypes, - srcSub.asmReturnvaluesRegisters, + srcSub.asmReturnvaluesRegisters.zip(srcSub.returntypes), srcSub.inline, srcSub.position) - sub.parameters.forEach { it.first.parent=sub } + sub.parameters.forEach { it.second.parent=sub } if(srcSub.asmAddress==null) { var combinedTrueAsm = "" @@ -329,10 +326,11 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable var returntype = srcSub.returntypes.singleOrNull() if(returntype==DataType.STR) returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore. + + // do not bother about the 'inline' hint of the source subroutine. val sub = PtSub(srcSub.name, srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) }, returntype, - srcSub.inline, srcSub.position) sub.parameters.forEach { it.parent=sub } makeScopeVarsDecls(vardecls).forEach { sub.add(it) } @@ -346,7 +344,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable return when(srcVar.type) { VarDeclType.VAR -> { val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null - PtVariable(srcVar.name, srcVar.datatype, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) + PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) } VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position) VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) @@ -385,8 +383,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable } private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer { - val type = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } - val array = PtArrayIndexer(type, srcArr.position) + val arrayVarType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val array = PtArrayIndexer(arrayVarType, srcArr.position) array.add(transform(srcArr.arrayvar)) array.add(transformExpression(srcArr.indexer.indexExpr)) return array @@ -464,6 +462,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable private fun transform(srcCast: TypecastExpression): PtTypeCast { val cast = PtTypeCast(srcCast.type, srcCast.position) cast.add(transformExpression(srcCast.expression)) + require(cast.type!=cast.value.type) return cast } diff --git a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt index 99ec7c415..bff62d575 100644 --- a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt @@ -178,7 +178,7 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val leftAssignment = Assignment( AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position), expr.left.copy(), - AssignmentOrigin.BEFOREASMGEN, expr.position + AssignmentOrigin.ASMGEN, expr.position ) } if(separateRightExpr) { @@ -187,7 +187,7 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val rightAssignment = Assignment( AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position), expr.right.copy(), - AssignmentOrigin.BEFOREASMGEN, expr.position + AssignmentOrigin.ASMGEN, expr.position ) } return CondExprSimplificationResult( diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 32c435428..bd3396df6 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -153,7 +153,7 @@ internal class StatementReorderer(val program: Program, subroutine.statements .asSequence() .filterIsInstance() - .filter { it.subroutineParameter!=null && it.name in stringParamsByNames } + .filter { it.origin==VarDeclOrigin.SUBROUTINEPARAM && it.name in stringParamsByNames } .map { val newvar = VarDecl(it.type, it.origin, DataType.UWORD, it.zeropage, @@ -162,7 +162,6 @@ internal class StatementReorderer(val program: Program, null, false, it.sharedWithAsm, - stringParamsByNames.getValue(it.name), it.position ) IAstModification.ReplaceNode(it, newvar, subroutine) diff --git a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt deleted file mode 100644 index c78ec197a..000000000 --- a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt +++ /dev/null @@ -1,138 +0,0 @@ -package prog8.compiler.astprocessing - -import prog8.ast.Program -import prog8.ast.base.FatalAstException -import prog8.ast.expressions.* -import prog8.ast.statements.* -import prog8.ast.walk.IAstVisitor -import prog8.code.* -import prog8.code.core.ArrayDatatypes -import prog8.code.core.CompilationOptions -import prog8.code.core.DataType -import prog8.code.core.Position -import java.util.* - -internal class SymbolTableMaker(private val program: Program, private val options: CompilationOptions): IAstVisitor { - - private val st = SymbolTable() - private val scopestack = Stack() - private var dontReinitGlobals = false - - fun make(): SymbolTable { - scopestack.clear() - st.children.clear() - dontReinitGlobals = options.dontReinitGlobals - this.visit(program) - program.builtinFunctions.names.forEach { - val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY) - st.add(node) - } - require(scopestack.isEmpty()) - return st - } - - override fun visit(block: Block) { - val node = StNode(block.name, StNodeType.BLOCK, block.position) - st.add(node) - scopestack.push(node) - super.visit(block) - scopestack.pop() - // st.origAstLinks[block] = node - } - - override fun visit(subroutine: Subroutine) { - if(subroutine.asmAddress!=null) { - val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) } - val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmReturnvaluesRegisters, subroutine.position) - scopestack.peek().add(node) - // st.origAstLinks[subroutine] = node - } else { - val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) } - val returnType = if(subroutine.returntypes.isEmpty()) null else subroutine.returntypes.first() - val node = StSub(subroutine.name, parameters, returnType, subroutine.position) - scopestack.peek().add(node) - scopestack.push(node) - super.visit(subroutine) - scopestack.pop() - // st.origAstLinks[subroutine] = node - } - } - - override fun visit(decl: VarDecl) { - val node = - when(decl.type) { - VarDeclType.VAR -> { - var initialNumeric = (decl.value as? NumericLiteral)?.number - if(initialNumeric==0.0) - initialNumeric=null // variable will go into BSS and this will be set to 0 - val initialStringLit = decl.value as? StringLiteral - val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding) - val initialArrayLit = decl.value as? ArrayLiteral - val initialArray = makeInitialArray(initialArrayLit) - if(decl.isArray && decl.datatype !in ArrayDatatypes) - throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}") - val numElements = - if(decl.isArray) - decl.arraysize!!.constIndex() - else if(initialStringLit!=null) - initialStringLit.value.length+1 // include the terminating 0-byte - else - null - val bss = if(decl.datatype==DataType.STR) - false - else if(decl.isArray) - initialArray.isNullOrEmpty() - else - initialNumeric == null - StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, decl.position) - } - VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position) - VarDeclType.MEMORY -> { - val numElements = - if(decl.datatype in ArrayDatatypes) - decl.arraysize!!.constIndex() - else null - StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, decl.position) - } - } - scopestack.peek().add(node) - // st.origAstLinks[decl] = node - } - - private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? { - if(arrayLit==null) - return null - return arrayLit.value.map { - when(it){ - is AddressOf -> { - val scopedName = it.identifier.targetNameAndType(program).first - StArrayElement(null, scopedName) - } - is IdentifierReference -> { - val scopedName = it.targetNameAndType(program).first - StArrayElement(null, scopedName) - } - is NumericLiteral -> StArrayElement(it.number, null) - else -> throw FatalAstException("weird element dt in array literal") - } - }.toList() - } - - override fun visit(label: Label) { - val node = StNode(label.name, StNodeType.LABEL, label.position) - scopestack.peek().add(node) - // st.origAstLinks[label] = node - } - - override fun visit(bfc: BuiltinFunctionCall) { - if(bfc.name=="memory") { - // memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable - val name = (bfc.args[0] as StringLiteral).value - require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} - val size = (bfc.args[1] as NumericLiteral).number.toUInt() - val align = (bfc.args[2] as NumericLiteral).number.toUInt() - st.add(StMemorySlab("prog8_memoryslab_$name", size, align, bfc.position)) - } - super.visit(bfc) - } -} diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index fa03fab27..83a06efa7 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -9,7 +9,6 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* -import prog8.compiler.BuiltinFunctions class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() { diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 48d52d8b2..926197ee5 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -40,7 +40,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } val sourceDt = typecast.expression.inferType(program) - if(sourceDt istype typecast.type) + if(sourceDt istype typecast.type || (sourceDt istype DataType.BOOL && typecast.type==DataType.UBYTE)) return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) if(parent is Assignment) { @@ -62,6 +62,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } override fun after(assignment: Assignment, parent: Node): Iterable { + if(assignment.target isSameAs assignment.value) { + // remove assignment to self + return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) + } + // remove duplicated assignments, but not if it's a memory mapped IO register val isIO = try { assignment.target.isIOAddress(options.compTarget.machine) diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 2fa6a9f12..b94e14266 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -5,11 +5,7 @@ import prog8.ast.Program import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor -import prog8.code.core.ByteDatatypes -import prog8.code.core.DataType -import prog8.code.core.IErrorReporter -import prog8.code.core.Position -import prog8.compiler.BuiltinFunctions +import prog8.code.core.* internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor { diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt index f7fc4fa36..4e1acf0a3 100644 --- a/compiler/test/TestBuiltinFunctions.kt +++ b/compiler/test/TestBuiltinFunctions.kt @@ -3,31 +3,19 @@ package prog8tests import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import prog8.ast.expressions.NumericLiteral +import prog8.ast.statements.Assignment +import prog8.code.core.BuiltinFunctions import prog8.code.core.DataType import prog8.code.core.NumericDatatypesNoBool import prog8.code.core.RegisterOrPair import prog8.code.target.Cx16Target -import prog8.compiler.BuiltinFunctions import prog8tests.helpers.compileText class TestBuiltinFunctions: FunSpec({ - test("push pop") { - val src=""" - main { - sub start () { - pushw(cx16.r0) - push(cx16.r1L) - pop(cx16.r1L) - popw(cx16.r0) - } - }""" - compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null - } - test("pure func with fixed type") { val func = BuiltinFunctions.getValue("sgn") - func.name shouldBe "sgn" func.parameters.size shouldBe 1 func.parameters[0].name shouldBe "value" func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool @@ -46,7 +34,6 @@ class TestBuiltinFunctions: FunSpec({ test("not-pure func with varying result value type") { val func = BuiltinFunctions.getValue("cmp") - func.name shouldBe "cmp" func.parameters.size shouldBe 2 func.pure shouldBe false func.returnType shouldBe null @@ -60,7 +47,6 @@ class TestBuiltinFunctions: FunSpec({ test("func without return type") { val func = BuiltinFunctions.getValue("poke") - func.name shouldBe "poke" func.parameters.size shouldBe 2 func.parameters[0].name shouldBe "address" func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD) @@ -81,5 +67,44 @@ class TestBuiltinFunctions: FunSpec({ conv.returns.floatFac1 shouldBe false conv.returns.reg shouldBe null } + + test("push pop") { + val src=""" + main { + sub start () { + pushw(cx16.r0) + push(cx16.r1L) + pop(cx16.r1L) + popw(cx16.r0) + } + }""" + compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null + } + + test("certain builtin functions should be compile time evaluated") { + val src=""" +main { + sub start() { + + uword[] array = [1,2,3] + str name = "hello" + cx16.r0L = len(array) + cx16.r0L = len(name) + cx16.r0L = sizeof(array) + cx16.r0 = mkword(200,100) + } +}""" + val result = compileText(Cx16Target(), false, src, writeAssembly = false) + val statements = result!!.compilerAst.entrypoint.statements + statements.size shouldBe 6 + val a1 = statements[2] as Assignment + val a2 = statements[3] as Assignment + val a3 = statements[4] as Assignment + val a4 = statements[5] as Assignment + (a1.value as NumericLiteral).number shouldBe 3.0 + (a2.value as NumericLiteral).number shouldBe 5.0 + (a3.value as NumericLiteral).number shouldBe 6.0 + (a4.value as NumericLiteral).number shouldBe 200*256+100 + } }) diff --git a/compiler/test/TestCallgraph.kt b/compiler/test/TestCallgraph.kt index d6b949f6b..fff7830da 100644 --- a/compiler/test/TestCallgraph.kt +++ b/compiler/test/TestCallgraph.kt @@ -27,11 +27,11 @@ class TestCallgraph: FunSpec({ } """ val result = compileText(C64Target(), false, sourcecode)!! - val graph = CallGraph(result.program) + val graph = CallGraph(result.compilerAst) graph.imports.size shouldBe 1 graph.importedBy.size shouldBe 1 - val toplevelModule = result.program.toplevelModule + val toplevelModule = result.compilerAst.toplevelModule val importedModule = graph.imports.getValue(toplevelModule).single() importedModule.name shouldBe "string" val importedBy = graph.importedBy.getValue(importedModule).single() @@ -46,7 +46,7 @@ class TestCallgraph: FunSpec({ graph.calls shouldNotContainKey sub graph.calledBy shouldNotContainKey sub - if(sub === result.program.entrypoint) + if(sub === result.compilerAst.entrypoint) withClue("start() should always be marked as used to avoid having it removed") { graph.unused(sub) shouldBe false } @@ -68,11 +68,11 @@ class TestCallgraph: FunSpec({ } """ val result = compileText(C64Target(), false, sourcecode)!! - val graph = CallGraph(result.program) + val graph = CallGraph(result.compilerAst) graph.imports.size shouldBe 1 graph.importedBy.size shouldBe 1 - val toplevelModule = result.program.toplevelModule + val toplevelModule = result.compilerAst.toplevelModule val importedModule = graph.imports.getValue(toplevelModule).single() importedModule.name shouldBe "string" val importedBy = graph.importedBy.getValue(importedModule).single() @@ -111,7 +111,7 @@ class TestCallgraph: FunSpec({ } """ val result = compileText(C64Target(), false, sourcecode)!! - val graph = CallGraph(result.program) + val graph = CallGraph(result.compilerAst) graph.allIdentifiers.size shouldBeGreaterThanOrEqual 5 val empties = graph.allIdentifiers.keys.filter { it.nameInSource==listOf("empty") } empties.size shouldBe 3 diff --git a/compiler/test/TestCompilerOnCharLit.kt b/compiler/test/TestCompilerOnCharLit.kt index 0f8238952..d345fbda9 100644 --- a/compiler/test/TestCompilerOnCharLit.kt +++ b/compiler/test/TestCompilerOnCharLit.kt @@ -33,7 +33,7 @@ class TestCompilerOnCharLit: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val funCall = startSub.statements.filterIsInstance()[0] @@ -57,7 +57,7 @@ class TestCompilerOnCharLit: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val funCall = startSub.statements.filterIsInstance()[0] @@ -92,7 +92,7 @@ class TestCompilerOnCharLit: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val funCall = startSub.statements.filterIsInstance()[0] diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 1ac5fd6c9..83232ca54 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -188,8 +188,8 @@ class TestCompilerOnExamplesVirtual: FunSpec({ val (displayName, filepath) = prepareTestFiles(it, false, target) test(displayName) { val src = filepath.readText() - compileText(target, false, src, writeAssembly = true) shouldNotBe null - compileText(target, false, src, writeAssembly = true) shouldNotBe null + compileText(target, true, src, writeAssembly = true) shouldNotBe null + compileText(target, true, src, writeAssembly = true) shouldNotBe null } } }) diff --git a/compiler/test/TestCompilerOnImportsAndIncludes.kt b/compiler/test/TestCompilerOnImportsAndIncludes.kt index dd3f71743..57601db20 100644 --- a/compiler/test/TestCompilerOnImportsAndIncludes.kt +++ b/compiler/test/TestCompilerOnImportsAndIncludes.kt @@ -30,7 +30,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({ val platform = Cx16Target() val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val strLits = startSub.statements .filterIsInstance() @@ -52,7 +52,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({ val platform = Cx16Target() val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val args = startSub.statements .filterIsInstance() diff --git a/compiler/test/TestCompilerOnRanges.kt b/compiler/test/TestCompilerOnRanges.kt index 0f4138510..e6cce92e4 100644 --- a/compiler/test/TestCompilerOnRanges.kt +++ b/compiler/test/TestCompilerOnRanges.kt @@ -40,7 +40,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val decl = startSub .statements.filterIsInstance()[0] @@ -72,7 +72,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val decl = startSub .statements.filterIsInstance()[0] @@ -143,7 +143,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val iterable = startSub .statements.filterIsInstance() @@ -177,7 +177,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val rangeExpr = startSub .statements.filterIsInstance() @@ -203,7 +203,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val rangeExpr = startSub .statements.filterIsInstance() @@ -247,7 +247,7 @@ class TestCompilerOnRanges: FunSpec({ } """)!! - val program = result.program + val program = result.compilerAst val startSub = program.entrypoint val iterable = startSub .statements.filterIsInstance() @@ -279,7 +279,7 @@ class TestCompilerOnRanges: FunSpec({ } } """)!! - val statements = result.program.entrypoint.statements + val statements = result.compilerAst.entrypoint.statements val array = (statements[0] as VarDecl).value array shouldBe instanceOf() (array as ArrayLiteral).value.size shouldBe 26 @@ -301,7 +301,7 @@ class TestCompilerOnRanges: FunSpec({ } } """)!! - val statements = result.program.entrypoint.statements + val statements = result.compilerAst.entrypoint.statements val forloop = (statements.dropLast(1).last() as ForLoop) forloop.iterable shouldBe instanceOf() (forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY) diff --git a/compiler/test/TestImportedModulesOrderAndOptions.kt b/compiler/test/TestImportedModulesOrderAndOptions.kt index b3167a330..d4b6ef54d 100644 --- a/compiler/test/TestImportedModulesOrderAndOptions.kt +++ b/compiler/test/TestImportedModulesOrderAndOptions.kt @@ -28,9 +28,9 @@ main { } } """)!! - result.program.toplevelModule.name shouldStartWith "on_the_fly_test" + result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" - val moduleNames = result.program.modules.map { it.name } + val moduleNames = result.compilerAst.modules.map { it.name } withClue("main module must be first") { moduleNames[0] shouldStartWith "on_the_fly_test" } @@ -46,7 +46,7 @@ main { "prog8_lib" ) } - result.program.toplevelModule.name shouldStartWith "on_the_fly_test" + result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" } test("testCompilationOptionsCorrectFromMain") { @@ -63,8 +63,8 @@ main { } } """)!! - result.program.toplevelModule.name shouldStartWith "on_the_fly_test" - val options = determineCompilationOptions(result.program, C64Target()) + result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" + val options = determineCompilationOptions(result.compilerAst, C64Target()) options.floats shouldBe true options.zeropage shouldBe ZeropageType.DONTUSE options.noSysInit shouldBe true diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index f7f5a006d..f63616393 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -15,7 +15,6 @@ import prog8.code.core.Position import prog8.code.core.SourceCode import prog8.code.core.ZeropageWish import prog8.code.target.C64Target -import prog8.compiler.printProgram import prog8tests.helpers.DummyFunctions import prog8tests.helpers.DummyMemsizer import prog8tests.helpers.DummyStringEncoder @@ -28,7 +27,7 @@ class TestMemory: FunSpec({ fun wrapWithProgram(statements: List): Program { val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder) - val subroutine = Subroutine("test", mutableListOf(), emptyList(), statements.toMutableList(), false, Position.DUMMY) + val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements.toMutableList(), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) program.addModule(module) return program @@ -113,7 +112,7 @@ class TestMemory: FunSpec({ } fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { - val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY) + val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -147,12 +146,11 @@ class TestMemory: FunSpec({ target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) - printProgram(target.definingModule.program) target.isIOAddress(c64target.machine) shouldBe true } test("regular variable not in mapped IO ram on C64") { - val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -164,7 +162,7 @@ class TestMemory: FunSpec({ test("memory mapped variable not in mapped IO ram on C64") { val address = 0x1000u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -176,7 +174,7 @@ class TestMemory: FunSpec({ test("memory mapped variable in mapped IO ram on C64") { val address = 0xd020u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -187,7 +185,7 @@ class TestMemory: FunSpec({ } test("array not in mapped IO ram") { - val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -200,7 +198,7 @@ class TestMemory: FunSpec({ test("memory mapped array not in mapped IO ram") { val address = 0x1000u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -213,7 +211,7 @@ class TestMemory: FunSpec({ test("memory mapped array in mapped IO ram") { val address = 0xd800u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) diff --git a/compiler/test/TestNumbers.kt b/compiler/test/TestNumbers.kt index 5157f00a5..a2f1e5980 100644 --- a/compiler/test/TestNumbers.kt +++ b/compiler/test/TestNumbers.kt @@ -14,7 +14,6 @@ import prog8.code.core.Position import prog8.code.core.toHex import prog8.code.target.C64Target import prog8.code.target.cbm.Mflpt5 -import prog8.compiler.printProgram import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText @@ -199,9 +198,8 @@ class TestNumbers: FunSpec({ } """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! - val statements = result.program.entrypoint.statements + val statements = result.compilerAst.entrypoint.statements statements.size shouldBe 8 - printProgram(result.program) (statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY) (statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY) (statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY) diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 81303b036..60437bef7 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -16,7 +16,6 @@ import prog8.code.core.DataType import prog8.code.core.Position import prog8.code.target.C64Target import prog8.code.target.Cx16Target -import prog8.compiler.printProgram import prog8tests.helpers.* @@ -32,10 +31,10 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), true, sourcecode)!! - val toplevelModule = result.program.toplevelModule + val toplevelModule = result.compilerAst.toplevelModule val mainBlock = toplevelModule.statements.single() as Block val startSub = mainBlock.statements.single() as Subroutine - result.program.entrypoint shouldBeSameInstanceAs startSub + result.compilerAst.entrypoint shouldBeSameInstanceAs startSub withClue("only start sub should remain") { startSub.name shouldBe "start" } @@ -57,11 +56,11 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), true, sourcecode)!! - val toplevelModule = result.program.toplevelModule + val toplevelModule = result.compilerAst.toplevelModule val mainBlock = toplevelModule.statements.single() as Block val startSub = mainBlock.statements[0] as Subroutine val emptySub = mainBlock.statements[1] as Subroutine - result.program.entrypoint shouldBeSameInstanceAs startSub + result.compilerAst.entrypoint shouldBeSameInstanceAs startSub startSub.name shouldBe "start" emptySub.name shouldBe "empty" withClue("compiler has inserted return in empty subroutines") { @@ -131,7 +130,7 @@ class TestOptimization: FunSpec({ // cx16.r5s -= 1899 // cx16.r7s = llw // cx16.r7s += 99 - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 14 val addR0value = (stmts[5] as Assignment).value @@ -183,7 +182,7 @@ class TestOptimization: FunSpec({ // cx16.r4s = llw // cx16.r4s *= 90 // cx16.r4s /= 5 - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 13 val mulR0Value = (stmts[3] as Assignment).value @@ -225,7 +224,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), false, sourcecode)!! - val mainsub = result.program.entrypoint + val mainsub = result.compilerAst.entrypoint mainsub.statements.size shouldBe 10 val declTest = mainsub.statements[0] as VarDecl val declX1 = mainsub.statements[1] as VarDecl @@ -265,8 +264,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), false, src, writeAssembly = true)!! - printProgram(result.program) - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 8 val value1 = (stmts[4] as Assignment).value as BinaryExpression @@ -298,7 +296,7 @@ class TestOptimization: FunSpec({ bb = prog8_lib.retval_interm_b return */ - val st = result.program.entrypoint.statements + val st = result.compilerAst.entrypoint.statements st.size shouldBe 8 st.last() shouldBe instanceOf() var assign = st[3] as Assignment @@ -324,7 +322,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), optimize=false, src, writeAssembly = false)!! - val assignFF = result.program.entrypoint.statements.last() as Assignment + val assignFF = result.compilerAst.entrypoint.statements.last() as Assignment assignFF.isAugmentable shouldBe true assignFF.target.identifier!!.nameInSource shouldBe listOf("ff") val value = assignFF.value as BinaryExpression @@ -350,8 +348,8 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - result.program.entrypoint.statements.size shouldBe 7 - val alldecls = result.program.entrypoint.allDefinedSymbols.toList() + result.compilerAst.entrypoint.statements.size shouldBe 7 + val alldecls = result.compilerAst.entrypoint.allDefinedSymbols.toList() alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar") } @@ -373,11 +371,11 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - result.program.entrypoint.statements.size shouldBe 3 - val ifstmt = result.program.entrypoint.statements[0] as IfElse + result.compilerAst.entrypoint.statements.size shouldBe 3 + val ifstmt = result.compilerAst.entrypoint.statements[0] as IfElse ifstmt.truepart.statements.size shouldBe 1 (ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0") - val func2 = result.program.entrypoint.statements[2] as Subroutine + val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine func2.statements.size shouldBe 1 (func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0") } @@ -401,7 +399,6 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - printProgram(result.program) /* expected: ubyte z1 z1 = 10 @@ -418,7 +415,7 @@ class TestOptimization: FunSpec({ z6 = z1 z6 -= 5 */ - val statements = result.program.entrypoint.statements + val statements = result.compilerAst.entrypoint.statements statements.size shouldBe 14 val z1decl = statements[0] as VarDecl val z1init = statements[1] as Assignment @@ -469,7 +466,7 @@ class TestOptimization: FunSpec({ """ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 6 val assign=stmts.last() as Assignment (assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa") @@ -486,7 +483,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 6 val assign=stmts.last() as Assignment (assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa") @@ -505,7 +502,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 10 stmts.filterIsInstance().size shouldBe 5 stmts.filterIsInstance().size shouldBe 5 @@ -554,8 +551,8 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! - result.program.entrypoint.statements.size shouldBe 11 - result.program.entrypoint.statements.last() shouldBe instanceOf() + result.compilerAst.entrypoint.statements.size shouldBe 11 + result.compilerAst.entrypoint.statements.last() shouldBe instanceOf() } test("keep the value initializer assignment if the next one depends on it") { @@ -582,7 +579,7 @@ class TestOptimization: FunSpec({ xx = abs(xx) xx += 6 */ - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 8 stmts.filterIsInstance().size shouldBe 3 stmts.filterIsInstance().size shouldBe 5 @@ -611,7 +608,7 @@ class TestOptimization: FunSpec({ yy = 0 xx += 10 */ - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 7 stmts.filterIsInstance().size shouldBe 2 stmts.filterIsInstance().size shouldBe 5 @@ -638,7 +635,6 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - printProgram(result.program) /* expected result: ubyte[] auto_heap_var = [1,4,99,3] @@ -649,7 +645,7 @@ class TestOptimization: FunSpec({ if source in auto_heap_var thingy++ */ - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 6 val ifStmt = stmts[5] as IfElse val containment = ifStmt.condition as ContainmentCheck @@ -679,8 +675,7 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - printProgram(result.program) - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 5 val ifStmt = stmts[4] as IfElse ifStmt.condition shouldBe instanceOf() @@ -698,8 +693,7 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - printProgram(result.program) - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 5 val ifStmt = stmts[4] as IfElse ifStmt.condition shouldBe instanceOf() @@ -717,7 +711,7 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 5 val ifStmt = stmts[4] as IfElse ifStmt.condition shouldBe instanceOf() @@ -734,7 +728,7 @@ class TestOptimization: FunSpec({ } }""" val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! - val stmts = result.program.entrypoint.statements + val stmts = result.compilerAst.entrypoint.statements stmts.size shouldBe 3 } @@ -756,15 +750,15 @@ main { } }""" var result = compileText(Cx16Target(), true, srcX16, writeAssembly = true)!! - var statements = result.program.entrypoint.statements + var statements = result.compilerAst.entrypoint.statements statements.size shouldBe 9 (statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx") (statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0") (statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0") (statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0") - (statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff - (statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff - (statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff + (statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff + (statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff + (statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff val srcC64=""" main { @@ -783,15 +777,15 @@ main { } }""" result = compileText(C64Target(), true, srcC64, writeAssembly = true)!! - statements = result.program.entrypoint.statements + statements = result.compilerAst.entrypoint.statements statements.size shouldBe 9 (statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx") (statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL") (statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL") (statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL") - (statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0 - (statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0 - (statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0 + (statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0 + (statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0 + (statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0 } test("no crash on sorting unused array") { diff --git a/compiler/test/TestScoping.kt b/compiler/test/TestScoping.kt index e38b61f51..4ea5bd758 100644 --- a/compiler/test/TestScoping.kt +++ b/compiler/test/TestScoping.kt @@ -27,9 +27,9 @@ class TestScoping: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! - val module = result.program.toplevelModule + val module = result.compilerAst.toplevelModule module.parent shouldBe instanceOf() - module.program shouldBeSameInstanceAs result.program + module.program shouldBeSameInstanceAs result.compilerAst module.parent.parent shouldBe instanceOf() } @@ -46,7 +46,7 @@ class TestScoping: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! - val module = result.program.toplevelModule + val module = result.compilerAst.toplevelModule val mainBlock = module.statements.single() as Block val start = mainBlock.statements.single() as Subroutine val repeatbody = start.statements.filterIsInstance().single().body @@ -120,7 +120,7 @@ class TestScoping: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = true)!! - val module = result.program.toplevelModule + val module = result.compilerAst.toplevelModule val mainBlock = module.statements.single() as Block val start = mainBlock.statements.single() as Subroutine val labels = start.statements.filterIsInstance