6502 codegen on new Pt-AST.

This commit is contained in:
Irmen de Jong 2023-01-04 22:55:05 +01:00
parent 6ee270d9d8
commit 5e8f767642
23 changed files with 1351 additions and 1452 deletions

View File

@ -43,6 +43,10 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
else -> false else -> false
} }
} }
infix fun isSameAs(target: PtAssignTarget): Boolean {
TODO()
}
} }
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
@ -66,6 +70,8 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position)
return false return false
return type==other.type && children == other.children return type==other.type && children == other.children
} }
val size: Int = children.size
} }
@ -141,6 +147,11 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { 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 { init {
if(type==DataType.BOOL) if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position") throw IllegalArgumentException("bool should have become ubyte @$position")

View File

@ -3,6 +3,8 @@ package prog8.code.ast
import prog8.code.core.* import prog8.code.core.*
interface IPtSubroutine
class PtAsmSub( class PtAsmSub(
name: String, name: String,
val address: UInt?, val address: UInt?,
@ -12,7 +14,7 @@ class PtAsmSub(
val retvalRegisters: List<RegisterOrStatusflag>, val retvalRegisters: List<RegisterOrStatusflag>,
val inline: Boolean, val inline: Boolean,
position: Position position: Position
) : PtNamedNode(name, position) { ) : PtNamedNode(name, position), IPtSubroutine {
override fun printProperties() { override fun printProperties() {
print("$name inline=$inline") print("$name inline=$inline")
} }
@ -25,7 +27,7 @@ class PtSub(
val returntype: DataType?, val returntype: DataType?,
val inline: Boolean, val inline: Boolean,
position: Position position: Position
) : PtNamedNode(name, position) { ) : PtNamedNode(name, position), IPtSubroutine {
override fun printProperties() { override fun printProperties() {
print(name) print(name)
} }

View File

@ -25,7 +25,6 @@ compileTestKotlin {
dependencies { dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"

View File

@ -10,7 +10,6 @@
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component> </component>
</module> </module>

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,13 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.Program import prog8.code.ast.PtProgram
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.code.core.IMachineDefinition import prog8.code.core.IMachineDefinition
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations // note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int { internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: PtProgram): Int {
var numberOfOptimizations = 0 var numberOfOptimizations = 0
@ -129,7 +126,7 @@ private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<S
return mods return mods
} }
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> { private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: PtProgram): List<Modification> {
// Optimize sequential assignments of the same value to various targets (bytes, words, floats) // 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... // the float one is the one that requires 2*7=14 lines of code to check...
@ -327,7 +324,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
return mods return mods
} }
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> { private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: PtProgram): List<Modification> {
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y // 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: // if Y isn't modified in between we can omit the second LDY:
@ -369,7 +366,7 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
return mods return mods
} }
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> { private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: PtProgram): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
@ -439,7 +436,7 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""") private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
private fun getAddressArg(line: String, program: Program): UInt? { private fun getAddressArg(line: String, program: PtProgram): UInt? {
val loadArg = line.trimStart().substring(3).trim() val loadArg = line.trimStart().substring(3).trim()
return when { return when {
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16) loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
@ -450,15 +447,7 @@ private fun getAddressArg(line: String, program: Program): UInt? {
val identMatch = identifierRegex.find(loadArg) val identMatch = identifierRegex.find(loadArg)
if(identMatch!=null) { if(identMatch!=null) {
val identifier = identMatch.value val identifier = identMatch.value
val decl = program.toplevelModule.lookup(identifier.split('.')) as? VarDecl TODO("lookup symbol's value $identifier")
if(decl!=null) {
when(decl.type){
VarDeclType.VAR -> null
VarDeclType.CONST,
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
}
}
else null
} else null } else null
} }
else -> loadArg.substring(1).toUIntOrNull() else -> loadArg.substring(1).toUIntOrNull()

View File

@ -1,15 +1,12 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.expressions.ArrayIndexedExpression import prog8.code.ast.*
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.expressions.Expression
import prog8.ast.statements.Subroutine
import prog8.code.core.Cx16VirtualRegisters import prog8.code.core.Cx16VirtualRegisters
import prog8.code.core.RegisterOrPair import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag import prog8.code.core.RegisterOrStatusflag
fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> { fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
val order = mutableListOf<Int>() val order = mutableListOf<Int>()
// order is: // order is:
// 1) cx16 virtual word registers, // 1) cx16 virtual word registers,
@ -17,7 +14,7 @@ fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
// 3) single CPU registers (X last), except A, // 3) single CPU registers (X last), except A,
// 4) CPU Carry status flag // 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) // 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 args = sub.parameters.withIndex()
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters } val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters } val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters }
@ -37,23 +34,25 @@ fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
return order return order
} }
fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>, fun asmsub6502ArgsHaveRegisterClobberRisk(
paramRegisters: List<RegisterOrStatusflag>): Boolean { args: List<PtExpression>,
fun isClobberRisk(expr: Expression): Boolean { paramRegisters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>
): Boolean {
fun isClobberRisk(expr: PtExpression): Boolean {
when (expr) { when (expr) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
return paramRegisters.any { return paramRegisters.any {
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) it.second.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
} }
} }
is BuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
if (expr.name == "lsb" || expr.name == "msb") if (expr.name == "lsb" || expr.name == "msb")
return isClobberRisk(expr.args[0]) return isClobberRisk(expr.args[0])
if (expr.name == "mkword") if (expr.name == "mkword")
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1]) return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
return !expr.isSimple return !expr.isSimple()
} }
else -> return !expr.isSimple else -> return !expr.isSimple()
} }
} }

View File

@ -3,7 +3,6 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError import com.github.michaelbull.result.mapError
import prog8.ast.generatedLabelPrefix
import prog8.code.core.* import prog8.code.core.*
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
@ -104,7 +103,7 @@ internal class AssemblyProgram(
} }
private fun removeGeneratedLabelsFromMonlist() { private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""") val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
val lines = viceMonListFile.toFile().readLines() val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use { viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) { for (line in lines) {

View File

@ -1,49 +1,39 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall import prog8.code.ast.*
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.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
import prog8.compiler.BuiltinFunctions
import prog8.compiler.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen) { private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single()) translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
} }
internal fun translateFunctioncallStatement(fcall: BuiltinFunctionCallStatement) { internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
val func = BuiltinFunctions.getValue(fcall.name) translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
} }
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if (discardResult && func.pure) if (discardResult && fcall.hasNoSideEffects)
return // can just ignore the whole function call altogether return // can just ignore the whole function call altogether
if(discardResult && resultToStack) if(discardResult && resultToStack)
throw AssemblyError("cannot both discard the result AND put it onto stack") throw AssemblyError("cannot both discard the result AND put it onto stack")
val sscope = (fcall as Node).definingSubroutine val sscope = fcall.definingSub()!!
when (func.name) { when (fcall.name) {
"msb" -> funcMsb(fcall, resultToStack, resultRegister) "msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack, resultRegister) "lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister) "mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall) "ror" -> funcRor(fcall)
@ -59,16 +49,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0]) "push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0]) "pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
"pop" -> { "pop" -> {
require(fcall.args[0] is IdentifierReference) { 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 as Node).position}" "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) asmgen.popCpuStack(DataType.UBYTE, (fcall.args[0] as PtIdentifier).targetVarDecl(program)!!, fcall.definingSub())
} }
"popw" -> { "popw" -> {
require(fcall.args[0] is IdentifierReference) { 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 as Node).position}" "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) asmgen.popCpuStack(DataType.UWORD, (fcall.args[0] as PtIdentifier).targetVarDecl(program)!!, fcall.definingSub())
} }
"rsave" -> funcRsave() "rsave" -> funcRsave()
"rsavex" -> funcRsaveX() "rsavex" -> funcRsaveX()
@ -77,7 +67,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"cmp" -> funcCmp(fcall) "cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall) "callfar" -> funcCallFar(fcall)
"callrom" -> funcCallRom(fcall) "callrom" -> funcCallRom(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}") else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
} }
} }
@ -132,29 +122,29 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1") 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") if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time") throw AssemblyError("callfar only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt() val bank = fcall.args[0].asConstInteger()
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0 val address = fcall.args[1].asConstInteger() ?: 0
val argAddrArg = fcall.args[2] val argAddrArg = fcall.args[2]
if(bank==null) if(bank==null)
throw AssemblyError("callfar (jsrfar) bank has to be a constant") 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) assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word") asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
} }
if(argAddrArg.constValue(program)?.number == 0.0) { if(argAddrArg.asConstInteger() == 0) {
asmgen.out(""" asmgen.out("""
jsr cx16.jsrfar jsr cx16.jsrfar
+ .word ${address.toHex()} + .word ${address.toHex()}
.byte ${bank.toHex()}""") .byte ${bank.toHex()}""")
} else { } else {
when(argAddrArg) { when(argAddrArg) {
is AddressOf -> { is PtAddressOf -> {
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE) if(argAddrArg.identifier.type != DataType.UBYTE)
throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE") throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE")
asmgen.out(""" asmgen.out("""
lda ${asmgen.asmVariableName(argAddrArg.identifier)} lda ${asmgen.asmVariableName(argAddrArg.identifier)}
@ -163,7 +153,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
.byte ${bank.toHex()} .byte ${bank.toHex()}
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""") sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
} }
is NumericLiteral -> { is PtNumber -> {
asmgen.out(""" asmgen.out("""
lda ${argAddrArg.number.toHex()} lda ${argAddrArg.number.toHex()}
jsr cx16.jsrfar jsr cx16.jsrfar
@ -176,12 +166,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcCallRom(fcall: IFunctionCall) { private fun funcCallRom(fcall: PtBuiltinFunctionCall) {
if(asmgen.options.compTarget.name != "cx16") if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callrom only works on cx16 target at this time") throw AssemblyError("callrom only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt() val bank = fcall.args[0].asConstInteger()
val address = fcall.args[1].constValue(program)?.number?.toInt() val address = fcall.args[1].asConstInteger()
if(bank==null || address==null) if(bank==null || address==null)
throw AssemblyError("callrom requires constant arguments") throw AssemblyError("callrom requires constant arguments")
@ -191,7 +181,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("callrom bank must be <32") throw AssemblyError("callrom bank must be <32")
val argAddrArg = fcall.args[2] val argAddrArg = fcall.args[2]
if(argAddrArg.constValue(program)?.number == 0.0) { if(argAddrArg.asConstInteger() == 0) {
asmgen.out(""" asmgen.out("""
lda $01 lda $01
pha pha
@ -202,8 +192,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
sta $01""") sta $01""")
} else { } else {
when(argAddrArg) { when(argAddrArg) {
is AddressOf -> { is PtAddressOf -> {
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE) if(argAddrArg.identifier.type != DataType.UBYTE)
throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE") throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE")
asmgen.out(""" asmgen.out("""
lda $01 lda $01
@ -216,7 +206,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
pla pla
sta $01""") sta $01""")
} }
is NumericLiteral -> { is PtNumber -> {
asmgen.out(""" asmgen.out("""
lda $01 lda $01
pha pha
@ -233,46 +223,44 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcCmp(fcall: IFunctionCall) { private fun funcCmp(fcall: PtBuiltinFunctionCall) {
val arg1 = fcall.args[0] val arg1 = fcall.args[0]
val arg2 = fcall.args[1] val arg2 = fcall.args[1]
val dt1 = arg1.inferType(program).getOrElse { throw AssemblyError("unknown dt") } if(arg1.type in ByteDatatypes) {
val dt2 = arg2.inferType(program).getOrElse { throw AssemblyError("unknown dt") } if(arg2.type in ByteDatatypes) {
if(dt1 in ByteDatatypes) {
if(dt2 in ByteDatatypes) {
when (arg2) { when (arg2) {
is IdentifierReference -> { is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}") asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
} }
is NumericLiteral -> { is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number.toInt()}") asmgen.out(" cmp #${arg2.number.toInt()}")
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if(arg2.addressExpression is NumericLiteral) { if(arg2.address is PtNumber) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}") asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
} else { } else {
if(arg1.isSimple) { if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else { } else {
asmgen.pushCpuStack(DataType.UBYTE, arg1) 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.definingSub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
} }
} }
} }
else -> { else -> {
if(arg1.isSimple) { if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else { } else {
asmgen.pushCpuStack(DataType.UBYTE, arg1) 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.definingSub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
} }
} }
@ -280,10 +268,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} else } else
throw AssemblyError("args for cmp() should have same dt") throw AssemblyError("args for cmp() should have same dt")
} else { } else {
// dt1 is a word // arg1 is a word
if(dt2 in WordDatatypes) { if(arg2.type in WordDatatypes) {
when (arg2) { when (arg2) {
is IdentifierReference -> { is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cpy ${asmgen.asmVariableName(arg2)}+1 cpy ${asmgen.asmVariableName(arg2)}+1
@ -291,7 +279,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
cmp ${asmgen.asmVariableName(arg2)} cmp ${asmgen.asmVariableName(arg2)}
+""") +""")
} }
is NumericLiteral -> { is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cpy #>${arg2.number.toInt()} cpy #>${arg2.number.toInt()}
@ -300,8 +288,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
+""") +""")
} }
else -> { else -> {
if(arg1.isSimple) { if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingSub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cpy P8ZP_SCRATCH_W1+1 cpy P8ZP_SCRATCH_W1+1
@ -310,7 +298,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
+""") +""")
} else { } else {
asmgen.pushCpuStack(DataType.UWORD, arg1) 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.definingSub())
asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(""" asmgen.out("""
@ -326,14 +314,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult || fcall !is BuiltinFunctionCall) if(discardResult || fcall !is PtBuiltinFunctionCall)
throw AssemblyError("should not discard result of memory allocation at $fcall") 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}"} 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) val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
slabname.linkParents(fcall) val addressOf = PtAddressOf(fcall.position)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position)) addressOf.add(slabname)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
val target = val target =
if(resultToStack) if(resultToStack)
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null) AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
@ -343,7 +332,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
@ -353,13 +342,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcReverse(fcall: IFunctionCall) { private fun funcReverse(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is IdentifierReference) { if (variable is PtIdentifier) {
val decl = variable.targetVarDecl(program)!! val decl = variable.targetVarDecl(program)!!
val varName = asmgen.asmVariableName(variable) val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraysize!!.constIndex() val numElements = decl.arraySize!!
when (decl.datatype) { when (decl.type) {
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out(""" asmgen.out("""
lda #<$varName lda #<$varName
@ -392,13 +381,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcSort(fcall: IFunctionCall) { private fun funcSort(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is IdentifierReference) { if (variable is PtIdentifier) {
val decl = variable.targetVarDecl(program)!! val decl = variable.targetVarDecl(program)!!
val varName = asmgen.asmVariableName(variable) val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraysize!!.constIndex() val numElements = decl.arraySize!!
when (decl.datatype) { when (decl.type) {
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out(""" asmgen.out("""
lda #<$varName lda #<$varName
@ -406,7 +395,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda #$numElements""") 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 -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out(""" asmgen.out("""
@ -415,7 +404,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda #$numElements""") 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") DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
@ -424,26 +413,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("weird type") throw AssemblyError("weird type")
} }
private fun funcRor2(fcall: IFunctionCall) { private fun funcRor2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
val dt = what.inferType(program) when (what.type) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'b') translateRolRorArrayArgs(what.variable, what, "ror2", 'b')
asmgen.out(" jsr prog8_lib.ror2_array_ub") asmgen.out(" jsr prog8_lib.ror2_array_ub")
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if (what.addressExpression is NumericLiteral) { if (what.address is PtNumber) {
val number = (what.addressExpression as NumericLiteral).number val number = (what.address as PtNumber).number
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}") asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
} else { } else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.ror2_mem_ub") asmgen.out(" jsr prog8_lib.ror2_mem_ub")
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable") asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
} }
@ -452,11 +440,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'w') translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw") asmgen.out(" jsr prog8_lib.ror2_array_uw")
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ") asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
} }
@ -467,26 +455,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcRor(fcall: IFunctionCall) { private fun funcRor(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
val dt = what.inferType(program) when (what.type) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'b') translateRolRorArrayArgs(what.variable, what, "ror", 'b')
asmgen.out(" jsr prog8_lib.ror_array_ub") asmgen.out(" jsr prog8_lib.ror_array_ub")
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if (what.addressExpression is NumericLiteral) { if (what.address is PtNumber) {
val number = (what.addressExpression as NumericLiteral).number val number = (what.address as PtNumber).number
asmgen.out(" ror ${number.toHex()}") asmgen.out(" ror ${number.toHex()}")
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(""" asmgen.out("""
@ -495,7 +482,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
+ ror ${'$'}ffff,x ; modified""") + ror ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} else { } else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
sta (+) + 1 sta (+) + 1
sty (+) + 2 sty (+) + 2
@ -503,7 +490,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable") asmgen.out(" ror $variable")
} }
@ -512,11 +499,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'w') translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw") asmgen.out(" jsr prog8_lib.ror_array_uw")
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+1 | ror $variable") asmgen.out(" ror $variable+1 | ror $variable")
} }
@ -527,26 +514,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcRol2(fcall: IFunctionCall) { private fun funcRol2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
val dt = what.inferType(program) when (what.type) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'b') translateRolRorArrayArgs(what.variable, what, "rol2", 'b')
asmgen.out(" jsr prog8_lib.rol2_array_ub") asmgen.out(" jsr prog8_lib.rol2_array_ub")
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if (what.addressExpression is NumericLiteral) { if (what.address is PtNumber) {
val number = (what.addressExpression as NumericLiteral).number val number = (what.address as PtNumber).number
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}") asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
} else { } else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.rol2_mem_ub") asmgen.out(" jsr prog8_lib.rol2_mem_ub")
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable") asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
} }
@ -555,11 +541,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'w') translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw") asmgen.out(" jsr prog8_lib.rol2_array_uw")
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ") asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
} }
@ -570,26 +556,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcRol(fcall: IFunctionCall) { private fun funcRol(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
val dt = what.inferType(program) when (what.type) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'b') translateRolRorArrayArgs(what.variable, what, "rol", 'b')
asmgen.out(" jsr prog8_lib.rol_array_ub") asmgen.out(" jsr prog8_lib.rol_array_ub")
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if (what.addressExpression is NumericLiteral) { if (what.address is PtNumber) {
val number = (what.addressExpression as NumericLiteral).number val number = (what.address as PtNumber).number
asmgen.out(" rol ${number.toHex()}") asmgen.out(" rol ${number.toHex()}")
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(""" asmgen.out("""
@ -598,7 +583,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
+ rol ${'$'}ffff,x ; modified""") + rol ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} else { } else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
sta (+) + 1 sta (+) + 1
sty (+) + 2 sty (+) + 2
@ -606,7 +591,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable") asmgen.out(" rol $variable")
} }
@ -615,11 +600,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'w') translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw") asmgen.out(" jsr prog8_lib.rol_array_uw")
} }
is IdentifierReference -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1") asmgen.out(" rol $variable | rol $variable+1")
} }
@ -630,22 +615,24 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) { private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) { if(arrayvar.type==DataType.UWORD) {
if(dt!='b') if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt") throw AssemblyError("non-array var indexing requires bytes dt")
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null) asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
} else { } 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)
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?) { private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt.getOr(DataType.UNDEFINED)) { when (dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack") DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack") DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack") DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
@ -654,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
} else { } else {
when (dt.getOr(DataType.UNDEFINED)) { when (dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A") 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.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A") DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
@ -666,30 +653,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: PtSub?) {
outputAddressAndLenghtOfArray(fcall.args[0]) outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt.getOr(DataType.UNDEFINED)) { when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack") 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_${function.name}_w_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_${function.name}_f_stack") DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
} else { } else {
when (dt.getOr(DataType.UNDEFINED)) { when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0") 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_${function.name}_w_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_${function.name}_f_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") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A) 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?) { private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED) val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0") DataType.UBYTE -> asmgen.out(" ldy #0")
@ -710,19 +697,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcPokeW(fcall: IFunctionCall) { private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
when(val addrExpr = fcall.args[0]) { when(val addrExpr = fcall.args[0]) {
is NumericLiteral -> { is PtNumber -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
val addr = addrExpr.number.toHex() val addr = addrExpr.number.toHex()
asmgen.out(" sta $addr | sty ${addr}+1") asmgen.out(" sta $addr | sty ${addr}+1")
return return
} }
is IdentifierReference -> { is PtIdentifier -> {
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(""" asmgen.out("""
@ -742,14 +729,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
return return
} }
} }
is BinaryExpression -> { is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) { if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference) val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) { if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) 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(""" asmgen.out("""
ldy #$index ldy #$index
sta ($varname),y sta ($varname),y
@ -769,13 +756,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.func_pokew") 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]) { when(val addrExpr = fcall.args[0]) {
is NumericLiteral -> { is PtNumber -> {
val addr = addrExpr.number.toHex() val addr = addrExpr.number.toHex()
asmgen.out(" lda $addr | ldy ${addr}+1") asmgen.out(" lda $addr | ldy ${addr}+1")
} }
is IdentifierReference -> { is PtIdentifier -> {
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
@ -800,12 +787,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.func_peekw") asmgen.out(" jsr prog8_lib.func_peekw")
} }
} }
is BinaryExpression -> { is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) { if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference) val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) { if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
// pointervar is already in the zero page, no need to copy // 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(""" asmgen.out("""
ldy #$index ldy #$index
lda ($varname),y lda ($varname),y
@ -845,7 +832,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) { if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
@ -854,12 +841,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
val reg = resultRegister ?: RegisterOrPair.AY val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0]) var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) { if(!needAsave) {
val mr0 = fcall.args[0] as? DirectMemoryRead val mr0 = fcall.args[0] as? PtMemoryByte
val mr1 = fcall.args[1] as? DirectMemoryRead val mr1 = fcall.args[1] as? PtMemoryByte
if (mr0 != null) if (mr0 != null)
needAsave = mr0.addressExpression !is NumericLiteral needAsave = mr0.address !is PtNumber
if (mr1 != null) if (mr1 != null)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral) needAsave = needAsave or (mr1.address !is PtNumber)
} }
when(reg) { when(reg) {
RegisterOrPair.AX -> { RegisterOrPair.AX -> {
@ -898,13 +885,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() val arg = fcall.args.single()
if (!arg.inferType(program).isWords) if (arg.type !in WordDatatypes)
throw AssemblyError("msb required word argument") throw AssemblyError("msb required word argument")
if (arg is NumericLiteral) if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away") throw AssemblyError("msb(const) should have been const-folded away")
if (arg is IdentifierReference) { if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg) val sourceName = asmgen.asmVariableName(arg)
if(resultToStack) { if(resultToStack) {
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex") asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
@ -952,14 +939,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() val arg = fcall.args.single()
if (!arg.inferType(program).isWords) if (arg.type !in WordDatatypes)
throw AssemblyError("lsb required word argument") throw AssemblyError("lsb required word argument")
if (arg is NumericLiteral) if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away") throw AssemblyError("lsb(const) should have been const-folded away")
if (arg is IdentifierReference) { if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg) val sourceName = asmgen.asmVariableName(arg)
if(resultToStack) { if(resultToStack) {
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex") asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
@ -1013,13 +1000,13 @@ 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 // address in P8ZP_SCRATCH_W1, number of elements in A
arg as IdentifierReference arg as PtIdentifier
val arrayVar = arg.targetVarDecl(program)!! val arrayVar = arg.targetVarDecl(program)!!
if(!arrayVar.isArray) if(arrayVar.arraySize==null)
throw AssemblyError("length of non-array requested") throw AssemblyError("length of non-array requested")
val size = arrayVar.arraysize!!.constIndex()!! val size = arrayVar.arraySize!!
val identifierName = asmgen.asmVariableName(arg) val identifierName = asmgen.asmVariableName(arg)
asmgen.out(""" asmgen.out("""
lda #<$identifierName lda #<$identifierName
@ -1030,18 +1017,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
""") """)
} }
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) { private fun translateArguments(args: MutableList<PtExpression>, signature: FSignature, scope: PtSub?) {
val callConv = signature.callConvention(args.map { val callConv = signature.callConvention(args.map { it.type})
it.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
})
fun getSourceForFloat(value: Expression): AsmAssignSource { fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) { return when (value) {
is IdentifierReference -> { is PtIdentifier -> {
val addr = AddressOf(value, value.position) val addr = PtAddressOf(value.position)
addr.add(value)
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
} }
is NumericLiteral -> { is PtNumber -> {
throw AssemblyError("float literals should have been converted into autovar") throw AssemblyError("float literals should have been converted into autovar")
} }
else -> { else -> {
@ -1049,9 +1035,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("cannot use float arguments outside of a subroutine scope") throw AssemblyError("cannot use float arguments outside of a subroutine scope")
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position) val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val addr = AddressOf(variable, value.position) val addr = PtAddressOf(value.position)
addr.linkParents(value) addr.add(variable)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope) asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
} }
@ -1069,7 +1055,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
DataType.FLOAT -> getSourceForFloat(value) DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> { in PassByReferenceDatatypes -> {
// put the address of the argument in AY // put the address of the argument in AY
val addr = AddressOf(value as IdentifierReference, value.position) val addr = PtAddressOf(value.position)
addr.add(value)
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
} }
else -> { else -> {
@ -1085,7 +1072,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
DataType.FLOAT -> getSourceForFloat(value) DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> { in PassByReferenceDatatypes -> {
// put the address of the argument in AY // put the address of the argument in AY
val addr = AddressOf(value as IdentifierReference, value.position) val addr = PtAddressOf(value.position)
addr.add(value)
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
} }
else -> { else -> {

View File

@ -1,16 +1,15 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.Program import prog8.code.ast.*
import prog8.ast.expressions.*
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: Program, internal class ExpressionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val allocator: VariableAllocator) { private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code") @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) { if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position) asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
} }
@ -21,31 +20,31 @@ internal class ExpressionsAsmGen(private val program: Program,
// the rest of the methods are all PRIVATE // the rest of the methods are all PRIVATE
private fun translateExpressionInternal(expression: Expression) { private fun translateExpressionInternal(expression: PtExpression) {
when(expression) { when(expression) {
is PrefixExpression -> translateExpression(expression) is PtPrefix -> translateExpression(expression)
is BinaryExpression -> translateExpression(expression) is PtBinaryExpression -> translateExpression(expression)
is ArrayIndexedExpression -> translateExpression(expression) is PtArrayIndexer -> translateExpression(expression)
is TypecastExpression -> translateExpression(expression) is PtTypeCast -> translateExpression(expression)
is AddressOf -> translateExpression(expression) is PtAddressOf -> translateExpression(expression)
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true) is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is NumericLiteral -> translateExpression(expression) is PtNumber -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression) is PtIdentifier -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression) is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null) is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported") is PtContainmentCheck -> 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 PtArray, is PtString -> 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 PtRange -> 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 PtMachineRegister -> TODO("machine register expression node")
else -> TODO("missing expression asmgen for $expression") else -> TODO("missing expression asmgen for $expression")
} }
} }
private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) { private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation // only for use in nested expression evaluation
val sub = call.target.targetSubroutine(program)!! val sub = call.targetSubroutine(program)!!
asmgen.saveXbeforeCall(call) asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call, true) asmgen.translateFunctionCall(call, true)
if(sub.regXasResult()) { if(sub.regXasResult()) {
@ -54,7 +53,8 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
asmgen.restoreXafterCall(call) asmgen.restoreXafterCall(call)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) sub.
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) // TODO does regular sub also have asmReturnvaluesRegisters set?
for ((_, reg) in returns) { 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) // 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) { if (reg.registerOrPair != null) {
@ -133,9 +133,9 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
} }
private fun translateExpression(typecast: TypecastExpression) { private fun translateExpression(typecast: PtTypeCast) {
translateExpressionInternal(typecast.expression) translateExpressionInternal(typecast.value)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) { when(typecast.value.type) {
DataType.UBYTE, DataType.BOOL -> { DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) { when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {} 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) val name = asmgen.asmVariableName(expr.identifier)
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex") 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) { when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex") DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out(""" 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) val varname = asmgen.asmVariableName(expr)
when(expr.inferType(program).getOr(DataType.UNDEFINED)) { when(expr.type) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex") 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. // 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). // 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 leftDt = expr.left.type
val rightIDt = expr.right.inferType(program) val rightDt = expr.right.type
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") }
// see if we can apply some optimized routines // see if we can apply some optimized routines
when(expr.operator) { when(expr.operator) {
"+" -> { "+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = expr.left.constValue(program)?.number?.toInt() val leftVal = expr.left.asConstInteger()
val rightVal = expr.right.constValue(program)?.number?.toInt() val rightVal = expr.right.asConstInteger()
if (leftVal!=null && leftVal in -4..4) { if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(expr.right) translateExpressionInternal(expr.right)
if(rightDt in ByteDatatypes) { if(rightDt in ByteDatatypes) {
@ -318,7 +313,7 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
"-" -> { "-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { 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) if (rightVal!=null && rightVal in -4..4)
{ {
translateExpressionInternal(expr.left) 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) { if(amount!=null) {
translateExpressionInternal(expr.left) translateExpressionInternal(expr.left)
when (leftDt) { 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) { if(amount!=null) {
translateExpressionInternal(expr.left) translateExpressionInternal(expr.left)
if (leftDt in ByteDatatypes) { if (leftDt in ByteDatatypes) {
@ -450,13 +445,13 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
"*" -> { "*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = expr.left as? IdentifierReference val leftVar = expr.left as? PtIdentifier
val rightVar = expr.right as? IdentifierReference val rightVar = expr.right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar) if(leftVar!=null && rightVar!=null && leftVar==rightVar)
return translateSquared(leftVar, leftDt) return translateSquared(leftVar, leftDt)
} }
val value = expr.right.constValue(program) val value = expr.right as? PtNumber
if(value!=null) { if(value!=null) {
if(rightDt in IntegerDatatypes) { if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt() val amount = value.number.toInt()
@ -516,7 +511,7 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
"/" -> { "/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) { 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) { if(rightVal!=null && rightVal==2) {
translateExpressionInternal(expr.left) translateExpressionInternal(expr.left)
when (leftDt) { when (leftDt) {
@ -557,8 +552,8 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
in ComparisonOperators -> { in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) { if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number val rightVal = expr.right.asConstInteger()
if(rightVal==0.0) if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator) 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) { private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
if(expr.isSimple) { if(expr.isSimple()) {
if(operator=="!=") { if(operator=="!=") {
when (dt) { when (dt) {
in ByteDatatypes -> { in ByteDatatypes -> {
@ -641,7 +636,7 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
"<" -> { "<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD) if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position)) return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
when(dt) { when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b") DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w") 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) if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position)) return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
when(dt) { when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb") DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw") 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) val asmVar = asmgen.asmVariableName(variable)
when(dt) { when(dt) {
DataType.BYTE, DataType.UBYTE -> { 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") asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} }
private fun translateExpression(expr: PrefixExpression) { private fun translateExpression(expr: PtPrefix) {
translateExpressionInternal(expr.expression) translateExpressionInternal(expr.value)
val itype = expr.inferType(program)
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
when(expr.operator) { when(expr.operator) {
"+" -> {} "+" -> {}
"-" -> { "-" -> {
when(type) { when(expr.type) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b") in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w") in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f") 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 -> in ByteDatatypes ->
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
@ -729,22 +722,18 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
} }
private fun translateExpression(arrayExpr: ArrayIndexedExpression) { private fun translateExpression(arrayExpr: PtArrayIndexer) {
val elementIDt = arrayExpr.inferType(program) val elementDt = arrayExpr.type
if(!elementIDt.isKnown) val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
throw AssemblyError("unknown dt")
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!! if(arrayExpr.type==DataType.UWORD) {
if(arrayVarDecl.datatype==DataType.UWORD) {
// indexing a pointer var instead of a real array or string // indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes) if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt") 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") throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.arrayvar)) { if(asmgen.isZpVar(arrayExpr.variable)) {
asmgen.out(" lda ($arrayVarName),y") asmgen.out(" lda ($arrayVarName),y")
} else { } else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1") 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 return
} }
val constIndexNum = arrayExpr.indexer.constIndex() val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) { if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt) val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
when(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(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", 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 asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A

View File

@ -0,0 +1,91 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.CpuRegister
import prog8.code.core.RegisterOrPair
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 {
when(this) {
is PtAddressOf -> TODO()
is PtArray -> TODO()
is PtArrayIndexer -> TODO()
is PtBinaryExpression -> TODO()
is PtBuiltinFunctionCall -> TODO()
is PtContainmentCheck -> TODO()
is PtFunctionCall -> TODO()
is PtIdentifier -> TODO()
is PtMachineRegister -> TODO()
is PtMemoryByte -> TODO()
is PtNumber -> TODO()
is PtPrefix -> TODO()
is PtRange -> TODO()
is PtString -> TODO()
is PtTypeCast -> TODO()
}
}
internal fun PtIdentifier.targetStatement(program: PtProgram): PtNode? {
TODO("Not yet implemented")
}
internal fun PtIdentifier.targetVarDecl(program: PtProgram): PtVariable? =
this.targetStatement(program) as? PtVariable
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.retvalRegisters.any { it.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.second.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 = retvalRegisters.any { it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.AX }
return KeepAresult(false, saveAonReturn)
}
internal fun PtFunctionCall.targetSubroutine(program: PtProgram): IPtSubroutine? {
TODO()
}

View File

@ -1,46 +1,44 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.ast.Program import prog8.code.ast.PtForLoop
import prog8.ast.expressions.IdentifierReference import prog8.code.ast.PtIdentifier
import prog8.ast.expressions.RangeExpression import prog8.code.ast.PtProgram
import prog8.ast.statements.ForLoop import prog8.code.ast.PtRange
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.absoluteValue 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: AsmGen, private val zeropage: Zeropage) {
internal fun translate(stmt: ForLoop) { internal fun translate(stmt: PtForLoop) {
val iterableDt = stmt.iterable.inferType(program) val iterableDt = stmt.iterable.type
if(!iterableDt.isKnown)
throw AssemblyError("unknown dt")
when(stmt.iterable) { when(stmt.iterable) {
is RangeExpression -> { is PtRange -> {
val range = (stmt.iterable as RangeExpression).toConstantIntegerRange() val range = (stmt.iterable as PtRange).toConstantIntegerRange()
if(range==null) { if(range==null) {
translateForOverNonconstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as RangeExpression) translateForOverNonconstRange(stmt, iterableDt, stmt.iterable as PtRange)
} else { } else {
translateForOverConstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, range) translateForOverConstRange(stmt, iterableDt, range)
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
translateForOverIterableVar(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as IdentifierReference) translateForOverIterableVar(stmt, iterableDt, stmt.iterable as PtIdentifier)
} }
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable") 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) { private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
val modifiedLabel = program.makeLabel("for_modified") val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = program.makeLabel("for_modifiedb") val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val stepsize=range.step.constValue(program)!!.number.toInt() val stepsize=range.step.asConstInteger()!!
if(stepsize < -1) { if(stepsize < -1) {
val limit = range.to.constValue(program)?.number val limit = range.to.asConstInteger()
if(limit==0.0) 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") 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 +50,11 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
val incdec = if(stepsize==1) "inc" else "dec" val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar // 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.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
$modifiedLabel cmp #0 ; modified $modifiedLabel cmp #0 ; modified
@ -70,11 +68,11 @@ $modifiedLabel cmp #0 ; modified
// bytes, step >= 2 or <= -2 // bytes, step >= 2 or <= -2
// loop over byte range via loopvar // 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.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if(stepsize>0) { if(stepsize>0) {
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
@ -102,14 +100,14 @@ $modifiedLabel cmp #0 ; modified
// words, step 1 or -1 // words, step 1 or -1
stepsize == 1 || stepsize == -1 -> { stepsize == 1 || stepsize == -1 -> {
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname+1 lda $varname+1
$modifiedLabel cmp #0 ; modified $modifiedLabel cmp #0 ; modified
@ -136,14 +134,14 @@ $modifiedLabel2 cmp #0 ; modified
stepsize > 0 -> { stepsize > 0 -> {
// (u)words, step >= 2 // (u)words, step >= 2
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) { if (iterableDt == DataType.ARRAY_UW) {
asmgen.out(""" asmgen.out("""
@ -184,14 +182,14 @@ $endLabel""")
else -> { else -> {
// (u)words, step <= -2 // (u)words, step <= -2
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if(iterableDt==DataType.ARRAY_UW) { if(iterableDt==DataType.ARRAY_UW) {
asmgen.out(""" asmgen.out("""
@ -237,9 +235,9 @@ $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) { private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident) val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program)!! val decl = ident.targetVarDecl(program)!!
@ -252,8 +250,8 @@ $endLabel""")
sty $loopLabel+2 sty $loopLabel+2
$loopLabel lda ${65535.toHex()} ; modified $loopLabel lda ${65535.toHex()} ; modified
beq $endLabel beq $endLabel
sta ${asmgen.asmVariableName(stmt.loopVar)}""") sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
inc $loopLabel+1 inc $loopLabel+1
bne $loopLabel bne $loopLabel
@ -262,15 +260,15 @@ $loopLabel lda ${65535.toHex()} ; modified
$endLabel""") $endLabel""")
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
val length = decl.arraysize!!.constIndex()!! val length = decl.arraySize!!
val indexVar = program.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
$loopLabel sty $indexVar $loopLabel sty $indexVar
lda $iterableName,y lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.loopVar)}""") sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if(length<=255) { if(length<=255u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@ -285,7 +283,7 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16) { if(length>=16u) {
// allocate index var on ZP if possible // allocate index var on ZP if possible
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
@ -298,9 +296,9 @@ $loopLabel sty $indexVar
asmgen.out(endLabel) asmgen.out(endLabel)
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = decl.arraysize!!.constIndex()!! * 2 val length = decl.arraySize!! * 2u
val indexVar = program.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.loopVar) val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
$loopLabel sty $indexVar $loopLabel sty $indexVar
@ -308,8 +306,8 @@ $loopLabel sty $indexVar
sta $loopvarName sta $loopvarName
lda $iterableName+1,y lda $iterableName+1,y
sta $loopvarName+1""") sta $loopvarName+1""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if(length<=127) { if(length<=127u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@ -326,7 +324,7 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16) { if(length>=16u) {
// allocate index var on ZP if possible // allocate index var on ZP if possible
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
@ -346,7 +344,7 @@ $loopLabel sty $indexVar
asmgen.loopEndLabels.pop() 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) if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0") throw AssemblyError("empty range or step 0")
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) { if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
@ -359,18 +357,18 @@ $loopLabel sty $indexVar
} }
// not one of the easy cases, generate more complex code... // not one of the easy cases, generate more complex code...
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
when(iterableDt) { when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
// loop over byte range via loopvar, step >= 2 or <= -2 // loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
lda #${range.first} lda #${range.first}
sta $varname sta $varname
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
when (range.step) { when (range.step) {
0, 1, -1 -> { 0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt") throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
@ -430,7 +428,7 @@ $loopLabel""")
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar, step >= 2 or <= -2 // 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) { when (range.step) {
0, 1, -1 -> { 0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt") throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
@ -444,7 +442,7 @@ $loopLabel""")
sta $varname sta $varname
sty $varname+1 sty $varname+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
cmp #<${range.last} cmp #<${range.last}
@ -470,16 +468,16 @@ $loopLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
lda #${range.first} lda #${range.first}
sta $varname sta $varname
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
if (range.last == 255) { if (range.last == 255) {
asmgen.out(""" asmgen.out("""
inc $varname inc $varname
@ -496,16 +494,16 @@ $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
lda #${range.first} lda #${range.first}
sta $varname sta $varname
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
when (range.last) { when (range.last) {
0 -> { 0 -> {
asmgen.out(""" asmgen.out("""
@ -533,18 +531,18 @@ $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
lda #<${range.first} lda #<${range.first}
ldy #>${range.first} ldy #>${range.first}
sta $varname sta $varname
sty $varname+1 sty $varname+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
cmp #<${range.last} cmp #<${range.last}
@ -560,18 +558,18 @@ $loopLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
lda #<${range.first} lda #<${range.first}
ldy #>${range.first} ldy #>${range.first}
sta $varname sta $varname
sty $varname+1 sty $varname+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
cmp #<${range.last} cmp #<${range.last}
@ -588,10 +586,10 @@ $loopLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun assignLoopvar(stmt: ForLoop, range: RangeExpression) = private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
asmgen.assignExpressionToVariable( asmgen.assignExpressionToVariable(
range.from, range.from,
asmgen.asmVariableName(stmt.loopVar), asmgen.asmVariableName(stmt.variable),
stmt.loopVarDt(program).getOrElse { throw AssemblyError("unknown dt") }, stmt.variable.type,
stmt.definingSubroutine) stmt.definingSub())
} }

View File

@ -1,16 +1,6 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall import prog8.code.ast.*
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.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource import prog8.codegen.cpu6502.assignment.AsmAssignSource
import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.AsmAssignTarget
@ -18,51 +8,57 @@ import prog8.codegen.cpu6502.assignment.AsmAssignment
import prog8.codegen.cpu6502.assignment.TargetStorageKind 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: AsmGen) {
internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) { internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt) saveXbeforeCall(stmt)
translateFunctionCall(stmt, false) translateFunctionCall(stmt, false)
restoreXafterCall(stmt) restoreXafterCall(stmt)
// just ignore any result values from the function call. // just ignore any result values from the function call.
} }
internal fun saveXbeforeCall(stmt: IFunctionCall) { internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.name}")
if(sub.shouldSaveX()) { 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(sub is PtAsmSub) {
if(regSaveOnStack) 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
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry) if (regSaveOnStack)
else asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!) else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
} }
} }
internal fun restoreXafterCall(stmt: IFunctionCall) { internal fun restoreXafterCall(stmt: PtFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.name}")
if(sub.shouldSaveX()) { 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(sub is PtAsmSub) {
if(regSaveOnStack) 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
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn) if (regSaveOnStack)
else asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
} else
asmgen.restoreRegisterLocal(CpuRegister.X) 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==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (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, isExpression: Boolean) { // TODO remove isExpression unused parameter
// Output only the code to set up the parameters and perform the actual call // 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 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!! // 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) // (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 sub: IPtSubroutine = call.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.name}")
val subAsmName = asmgen.asmSymbolName(call.target) val subAsmName = asmgen.asmSymbolName(call.name)
if(sub.isAsmSubroutine) { if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call) argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) { if (sub.inline && asmgen.options.optimize) {
// inline the subroutine. // inline the subroutine.
@ -70,13 +66,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 // 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) // (this condition has been enforced by an ast check earlier)
asmgen.out(" \t; inlined routine follows: ${sub.name}") 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}") asmgen.out(" \t; inlined routine end: ${sub.name}")
} else { } else {
asmgen.out(" jsr $subAsmName") asmgen.out(" jsr $subAsmName")
} }
} }
else { else if(sub is PtSub) {
if(sub.inline) if(sub.inline)
throw AssemblyError("can only reliably inline asmsub routines at this time") throw AssemblyError("can only reliably inline asmsub routines at this time")
@ -100,84 +96,80 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
asmgen.out(" jsr $subAsmName") 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 // 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) { if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0]) argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().first), call.args[0])
} else { } else {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) { if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
registerArgsViaCpuStackEvaluation(call, sub) registerArgsViaCpuStackEvaluation(call, sub)
} else { } else {
asmsub6502ArgsEvalOrder(sub).forEach { asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it] val param = sub.parameters[it]
val arg = call.args[it] val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param), arg) argumentViaRegister(sub, IndexedValue(it, param.first), 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 // 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. // 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()) if(callee.parameters.isEmpty())
return return
// use the cpu hardware stack as intermediate storage for the arguments. // use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee) val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach { argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].type, call.args[it]) asmgen.pushCpuStack(callee.parameters[it].first.type, call.args[it])
} }
argOrder.forEach { argOrder.forEach {
val param = callee.parameters[it] val param = callee.parameters[it].first
val targetVar = callee.searchParameter(param.name)!! val targetVar = callee.searchParameter(param.name)!!
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine) asmgen.popCpuStack(param.type, targetVar, call.definingSub())
} }
} }
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) // pass parameter via a regular variable (not via registers)
val valueIDt = value.inferType(program) if(!isArgumentTypeCompatible(value.type, parameter.type))
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.type))
throw AssemblyError("argument type incompatible") 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) asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
} }
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) { private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
// pass argument via a register parameter // pass argument via a register parameter
val valueIDt = value.inferType(program) if(!isArgumentTypeCompatible(value.type, parameter.value.type))
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
throw AssemblyError("argument type incompatible") throw AssemblyError("argument type incompatible")
val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null) val paramRegister = if(registerOverride==null) sub.parameters[parameter.index].second else RegisterOrStatusflag(registerOverride, null)
val statusflag = paramRegister.statusflag val statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type val requiredDt = parameter.value.type
if(requiredDt!=valueDt) { if(requiredDt!=value.type) {
if(valueDt largerThan requiredDt) if(value.type largerThan requiredDt)
throw AssemblyError("can only convert byte values to word param types") throw AssemblyError("can only convert byte values to word param types")
} }
if (statusflag!=null) { if (statusflag!=null) {
if(requiredDt!=valueDt) if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required") throw AssemblyError("for statusflag, byte value is required")
if (statusflag == Statusflag.Pc) { if (statusflag == Statusflag.Pc) {
// this param needs to be set last, right before the jsr // 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 // for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) { when(value) {
is NumericLiteral -> { is PtNumber -> {
val carrySet = value.number.toInt() != 0 val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc") asmgen.out(if(carrySet) " sec" else " clc")
} }
is IdentifierReference -> { is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value) val sourceName = asmgen.asmVariableName(value)
asmgen.out(""" asmgen.out("""
pha pha
@ -202,10 +194,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else { else {
// via register or register pair // via register or register pair
register!! register!!
if(requiredDt largerThan valueDt) { if(requiredDt largerThan value.type) {
// we need to sign extend the source, do this via temporary word variable // we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub) 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) asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
} else { } else {
val target: AsmAssignTarget = val target: AsmAssignTarget =
@ -215,9 +207,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen) AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
} }
val src = if(valueDt in PassByReferenceDatatypes) { val src = if(value.type in PassByReferenceDatatypes) {
if(value is IdentifierReference) { if(value is PtIdentifier) {
val addr = AddressOf(value, Position.DUMMY) val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else { } else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)

View File

@ -1,23 +1,23 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.Program import prog8.code.ast.PtIdentifier
import prog8.ast.expressions.IdentifierReference import prog8.code.ast.PtNumber
import prog8.ast.expressions.NumericLiteral import prog8.code.ast.PtPostIncrDecr
import prog8.ast.statements.PostIncrDecr import prog8.code.ast.PtProgram
import prog8.code.core.* import prog8.code.core.*
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen) {
internal fun translate(stmt: PostIncrDecr) { internal fun translate(stmt: PtPostIncrDecr) {
val incr = stmt.operator=="++" val incr = stmt.operator=="++"
val targetIdent = stmt.target.identifier val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memoryAddress val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.arrayindexed val targetArrayIdx = stmt.target.array
val scope = stmt.definingSubroutine val scope = stmt.definingSub()
when { when {
targetIdent!=null -> { targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent) 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 ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> { in WordDatatypes -> {
if(incr) if(incr)
@ -38,12 +38,12 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
} }
} }
targetMemory!=null -> { targetMemory!=null -> {
when (val addressExpr = targetMemory.addressExpression) { when (val addressExpr = targetMemory.address) {
is NumericLiteral -> { is PtNumber -> {
val what = addressExpr.number.toHex() val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what") asmgen.out(if(incr) " inc $what" else " dec $what")
} }
is IdentifierReference -> { is PtIdentifier -> {
val what = asmgen.asmVariableName(addressExpr) val what = asmgen.asmVariableName(addressExpr)
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2") asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
if(incr) if(incr)
@ -62,9 +62,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
} }
} }
targetArrayIdx!=null -> { targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar) val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED) val elementDt = targetArrayIdx.type
val constIndex = targetArrayIdx.indexer.constIndex() val constIndex = targetArrayIdx.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) { when(elementDt) {

View File

@ -1,12 +1,10 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.statements.*
import prog8.code.* import prog8.code.*
import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.TargetStorageKind import prog8.codegen.cpu6502.assignment.TargetStorageKind
import prog8.compiler.CallGraph
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -20,7 +18,7 @@ import kotlin.math.absoluteValue
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!) * - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
*/ */
internal class ProgramAndVarsGen( internal class ProgramAndVarsGen(
val program: Program, val program: PtProgram,
val options: CompilationOptions, val options: CompilationOptions,
val errors: IErrorReporter, val errors: IErrorReporter,
private val symboltable: SymbolTable, private val symboltable: SymbolTable,
@ -30,20 +28,19 @@ internal class ProgramAndVarsGen(
private val zeropage: Zeropage private val zeropage: Zeropage
) { ) {
private val compTarget = options.compTarget private val compTarget = options.compTarget
private val callGraph = CallGraph(program, true) // TODO ???? private val callGraph = CallGraph(program, true)
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() } private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
internal fun generate() { internal fun generate() {
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
header() header()
val allBlocks = program.allBlocks val allBlocks = program.allBlocks()
if(allBlocks.first().name != "main") if(allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main'") throw AssemblyError("first block should be 'main'")
if(errors.noErrors()) { 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 // the global list of all floating point constants for the whole program
asmgen.out("; global float constants") asmgen.out("; global float constants")
@ -100,7 +97,7 @@ internal class ProgramAndVarsGen(
when(options.launcher) { when(options.launcher) {
CbmPrgLauncherType.BASIC -> { CbmPrgLauncherType.BASIC -> {
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) { 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("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
@ -183,28 +180,28 @@ internal class ProgramAndVarsGen(
asmgen.out("prog8_program_end\t; end of program label for progend()") 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("")
asmgen.out("; ---- block: '${block.name}' ----") asmgen.out("; ---- block: '${block.name}' ----")
if(block.address!=null) if(block.address!=null)
asmgen.out("* = ${block.address!!.toHex()}") asmgen.out("* = ${block.address!!.toHex()}")
else { else {
if("align_word" in block.options()) if(block.alignment==PtBlock.BlockAlignment.WORD)
asmgen.out("\t.align 2") 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("\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) asmgen.outputSourceLine(block)
createBlockVariables(block) createBlockVariables(block)
asmsubs2asm(block.statements) asmsubs2asm(block.children)
asmgen.out("") asmgen.out("")
val initializers = blockVariableInitializers.getValue(block) 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) } notInitializers.forEach { asmgen.translate(it) }
if(!options.dontReinitGlobals) { if(!options.dontReinitGlobals) {
@ -216,13 +213,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<String, StNode> = private fun getVars(scope: StNode): Map<String, StNode> =
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) } 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") } val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.BLOCK) require(scope.type==StNodeType.BLOCK)
val varsInBlock = getVars(scope) val varsInBlock = getVars(scope)
@ -247,12 +244,41 @@ internal class ProgramAndVarsGen(
nonZpVariables2asm(variables) nonZpVariables2asm(variables)
} }
internal fun translateSubroutine(sub: Subroutine) { internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) {
if(options.optimize) {
return
}
}
asmgen.out("")
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock()!!.forceOutput) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
asmStartScope = ".proc"
asmEndScope = ".pend"
}
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.children.forEach { asmgen.translate(it) }
asmgen.out(" $asmEndScope\n")
}
internal fun translateSubroutine(sub: PtSub) {
var onlyVariables = false var onlyVariables = false
if(sub.inline) { if(sub.inline) {
if(options.optimize) { if(options.optimize) {
if(sub.isAsmSubroutine || callGraph.unused(sub)) if(callGraph.unused(sub))
return return
// from an inlined subroutine only the local variables are generated, // from an inlined subroutine only the local variables are generated,
@ -266,7 +292,7 @@ internal class ProgramAndVarsGen(
val asmStartScope: String val asmStartScope: String
val asmEndScope: String val asmEndScope: String
if(sub.definingBlock.options().contains("force_output")) { if(sub.definingBlock()!!.forceOutput) {
asmStartScope = ".block" asmStartScope = ".block"
asmEndScope = ".bend" asmEndScope = ".bend"
} else { } else {
@ -274,95 +300,84 @@ internal class ProgramAndVarsGen(
asmEndScope = ".pend" asmEndScope = ".pend"
} }
if(sub.isAsmSubroutine) { asmgen.out("${sub.name}\t$asmStartScope")
if(sub.asmAddress!=null)
return // already done at the memvars section
// asmsub with most likely just an inline asm in it val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
asmgen.out("${sub.name}\t$asmStartScope") require(scope.type==StNodeType.SUBROUTINE)
sub.statements.forEach { asmgen.translate(it) } val varsInSubroutine = getVars(scope)
asmgen.out(" $asmEndScope\n")
} else {
// regular subroutine
asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName.joinToString(".")) { throw AssemblyError("lookup") } // Zeropage Variables
require(scope.type==StNodeType.SUBROUTINE) val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
val varsInSubroutine = getVars(scope) zeropagevars2asm(varnames)
// Zeropage Variables // MemDefs and Consts
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet() val mvs = varsInSubroutine
zeropagevars2asm(varnames) .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)
// MemDefs and Consts asmsubs2asm(sub.children)
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()
// the main.start subroutine is the program's entrypoint and should perform some initialization logic if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
if(sub.name=="start" && sub.definingBlock.name=="main") asmgen.out("; simple int arg(s) passed via register(s)")
entrypointInitialization() if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
asmgen.out("; simple int arg(s) passed via register(s)") if(dt in ByteDatatypes)
if(sub.parameters.size==1) { asmgen.assignRegister(RegisterOrPair.A, target)
val dt = sub.parameters[0].type else
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name) asmgen.assignRegister(RegisterOrPair.AY, target)
if(dt in ByteDatatypes) } else {
asmgen.assignRegister(RegisterOrPair.A, target) require(sub.parameters.size==2)
else // 2 simple byte args, first in A, second in Y
asmgen.assignRegister(RegisterOrPair.AY, target) val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
} else { val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
require(sub.parameters.size==2) asmgen.assignRegister(RegisterOrPair.A, target1)
// 2 simple byte args, first in A, second in Y asmgen.assignRegister(RegisterOrPair.Y, target2)
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")
} }
if(!onlyVariables) {
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() { private fun entrypointInitialization() {
@ -593,12 +608,12 @@ internal class ProgramAndVarsGen(
} }
} }
private fun asmsubs2asm(statements: List<Statement>) { private fun asmsubs2asm(statements: List<PtNode>) {
statements statements
.filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null } .filter { it is PtAsmSub && it.address!=null }
.forEach { asmsub -> .forEach { asmsub ->
asmsub as Subroutine asmsub as PtAsmSub
asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}") asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
} }
} }

View File

@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
allocateZeropageVariables() allocateZeropageVariables()
} }
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars // TODO as dotted string instead of list?
internal fun getFloatAsmConst(number: Double): String { internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number] val asmName = globalFloatConsts[number]

View File

@ -1,13 +1,11 @@
package prog8.codegen.cpu6502.assignment package prog8.codegen.cpu6502.assignment
import prog8.ast.Program import prog8.code.ast.*
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.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.asConstInteger
import prog8.codegen.cpu6502.targetSubroutine
import prog8.codegen.cpu6502.targetVarDecl
internal enum class TargetStorageKind { internal enum class TargetStorageKind {
@ -31,20 +29,20 @@ internal enum class SourceStorageKind {
internal class AsmAssignTarget(val kind: TargetStorageKind, internal class AsmAssignTarget(val kind: TargetStorageKind,
private val asmgen: AsmGen, private val asmgen: AsmGen,
val datatype: DataType, val datatype: DataType,
val scope: Subroutine?, val scope: IPtSubroutine?,
private val variableAsmName: String? = null, private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null, val array: PtArrayIndexer? = null,
val memory: DirectMemoryWrite? = null, val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = 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 { val asmVarname: String by lazy {
if (array == null) if (array == null)
variableAsmName!! variableAsmName!!
else else
asmgen.asmVariableName(array.arrayvar) asmgen.asmVariableName(array.variable)
} }
lateinit var origAssign: AsmAssignment lateinit var origAssign: AsmAssignment
@ -55,10 +53,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
companion object { companion object {
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget { fun fromAstAssignment(assign: PtAssignment, program: PtProgram, asmgen: AsmGen): AsmAssignTarget {
with(assign.target) { with(assign.target) {
val idt = inferType(program)
val dt = idt.getOrElse { throw AssemblyError("unknown dt") }
when { when {
identifier != null -> { identifier != null -> {
val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter
@ -69,19 +65,19 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
if(reg.statusflag!=null) if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly") throw AssemblyError("can't assign value to processor statusflag directly")
else else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this) return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingSub(), 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.definingSub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
} }
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this) array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingSub(), array = array, origAstTarget = this)
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this) memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingSub(), memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target") 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: AsmGen): AsmAssignTarget =
when(registers) { when(registers) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
@ -112,69 +108,65 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
internal class AsmAssignSource(val kind: SourceStorageKind, internal class AsmAssignSource(val kind: SourceStorageKind,
private val program: Program, private val program: PtProgram,
private val asmgen: AsmGen, private val asmgen: AsmGen,
val datatype: DataType, val datatype: DataType,
private val variableAsmName: String? = null, private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null, val array: PtArrayIndexer? = null,
val memory: DirectMemoryRead? = null, val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null, val register: RegisterOrPair? = null,
val number: NumericLiteral? = null, val number: PtNumber? = null,
val expression: Expression? = null val expression: PtExpression? = null
) )
{ {
val asmVarname: String val asmVarname: String
get() = if(array==null) get() = if(array==null)
variableAsmName!! variableAsmName!!
else else
asmgen.asmVariableName(array.arrayvar) asmgen.asmVariableName(array.variable)
companion object { companion object {
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource { fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program) val cv = value as? PtNumber
if(cv!=null) if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
return when(value) { return when(value) {
is NumericLiteral -> throw AssemblyError("should have been constant value") is PtNumber -> throw AssemblyError("should have been constant value")
is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation") is PtString -> 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 PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is IdentifierReference -> { is PtIdentifier -> {
val parameter = value.targetVarDecl(program)?.subroutineParameter val parameter = value.targetVarDecl(program)?.subroutineParameter
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine) if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}") 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) val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system // 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 regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase()) val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg) AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
} else { } 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) AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
} }
is ArrayIndexedExpression -> { is PtArrayIndexer -> {
val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") } AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
} }
is BuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
val returnType = value.inferType(program) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
} }
is FunctionCallExpression -> { is PtFunctionCall -> {
val sub = value.target.targetSubroutine(program)!! val sub = value.targetSubroutine(program)!!
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
?: throw AssemblyError("can't translate zero return values in assignment") ?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
} }
else -> { else -> {
val returnType = value.inferType(program) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
} }
} }
} }

View File

@ -1,24 +1,20 @@
package prog8.codegen.cpu6502.assignment package prog8.codegen.cpu6502.assignment
import prog8.ast.Program import prog8.code.ast.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.*
import prog8.codegen.cpu6502.VariableAllocator
import prog8.compiler.builtinFunctionReturnType
internal class AssignmentAsmGen(private val program: Program, internal class AssignmentAsmGen(private val program: PtProgram,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val allocator: VariableAllocator) { private val allocator: VariableAllocator) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator) private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
fun translate(assignment: Assignment) { fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) 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 target.origAssign = assign
if(assign.isAugmentable) if(assign.isAugmentable)
@ -64,17 +60,16 @@ internal class AssignmentAsmGen(private val program: Program,
SourceStorageKind.ARRAY -> { SourceStorageKind.ARRAY -> {
val value = assign.source.array!! val value = assign.source.array!!
val elementDt = assign.source.datatype val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.arrayvar) val arrayVarName = asmgen.asmVariableName(value.variable)
val arrayVarDecl = value.arrayvar.targetVarDecl(program)!! if(value.variable.type==DataType.UWORD) {
if(arrayVarDecl.datatype==DataType.UWORD) {
// indexing a pointer var instead of a real array or string // indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes) if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt") 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") throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(value.arrayvar)) { if(asmgen.isZpVar(value.variable)) {
asmgen.out(" lda ($arrayVarName),y") asmgen.out(" lda ($arrayVarName),y")
} else { } else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1") asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
@ -84,7 +79,7 @@ internal class AssignmentAsmGen(private val program: Program,
return return
} }
val constIndex = value.indexer.constIndex() val constIndex = value.index.asConstInteger()
if (constIndex!=null) { if (constIndex!=null) {
// constant array index value // constant array index value
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
@ -133,29 +128,29 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
SourceStorageKind.MEMORY -> { SourceStorageKind.MEMORY -> {
fun assignViaExprEval(expression: Expression) { fun assignViaExprEval(expression: PtExpression) {
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope) assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
} }
val value = assign.source.memory!! val value = assign.source.memory!!
when (value.addressExpression) { when (value.address) {
is NumericLiteral -> { is PtNumber -> {
val address = (value.addressExpression as NumericLiteral).number.toUInt() val address = (value.address as PtNumber).number.toUInt()
assignMemoryByte(assign.target, address, null) assignMemoryByte(assign.target, address, null)
} }
is IdentifierReference -> { is PtIdentifier -> {
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference) assignMemoryByte(assign.target, null, value.address as PtIdentifier)
} }
is BinaryExpression -> { is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) { if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
} else { } else {
assignViaExprEval(value.addressExpression) assignViaExprEval(value.address)
} }
} }
else -> assignViaExprEval(value.addressExpression) else -> assignViaExprEval(value.address)
} }
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
@ -173,17 +168,17 @@ internal class AssignmentAsmGen(private val program: Program,
private fun assignExpression(assign: AsmAssignment) { private fun assignExpression(assign: AsmAssignment) {
when(val value = assign.source.expression!!) { when(val value = assign.source.expression!!) {
is AddressOf -> { is PtAddressOf -> {
val sourceName = asmgen.asmSymbolName(value.identifier) val sourceName = asmgen.asmSymbolName(value.identifier)
assignAddressOf(assign.target, sourceName) assignAddressOf(assign.target, sourceName)
} }
is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber") is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
is IdentifierReference -> throw AssemblyError("source kind should have been variable") is PtIdentifier -> throw AssemblyError("source kind should have been variable")
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array") is PtArrayIndexer -> throw AssemblyError("source kind should have been array")
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") is PtMemoryByte -> throw AssemblyError("source kind should have been memory")
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value)
is FunctionCallExpression -> { is PtFunctionCall -> {
val sub = value.target.targetSubroutine(program)!! val sub = value.targetSubroutine(program)!!
asmgen.saveXbeforeCall(value) asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value, true) asmgen.translateFunctionCall(value, true)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
@ -245,14 +240,11 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
} }
is BuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
if(assign.target.register==null) { if(assign.target.register==null) {
// still need to assign the result to the target variable/etc. // still need to assign the result to the target variable/etc.
val returntype = builtinFunctionReturnType(value.name) when(builtinFunctionReturnType(value.name)) {
if(!returntype.isKnown)
throw AssemblyError("unknown dt")
when(returntype.getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A 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 in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
DataType.STR -> { DataType.STR -> {
@ -279,13 +271,13 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
} }
is PrefixExpression -> { is PtPrefix -> {
if(assign.target.array==null) { if(assign.target.array==null) {
// First assign the value to the target then apply the operator in place on the target. // First assign the value to the target then apply the operator in place on the target.
// This saves a temporary variable // This saves a temporary variable
translateNormalAssignment( translateNormalAssignment(
AsmAssignment( AsmAssignment(
AsmAssignSource.fromAstSource(value.expression, program, asmgen), AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, assign.target,
false, program.memsizer, assign.position false, program.memsizer, assign.position
) )
@ -301,11 +293,11 @@ internal class AssignmentAsmGen(private val program: Program,
assignPrefixedExpressionToArrayElt(assign) assignPrefixedExpressionToArrayElt(assign)
} }
} }
is ContainmentCheck -> { is PtContainmentCheck -> {
containmentCheckIntoA(value) containmentCheckIntoA(value)
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
} }
is BinaryExpression -> { is PtBinaryExpression -> {
if(!attemptAssignOptimizedBinexpr(value, assign)) { if(!attemptAssignOptimizedBinexpr(value, assign)) {
// All remaining binary expressions just evaluate via the stack for now. // 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, // (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
@ -318,7 +310,7 @@ internal class AssignmentAsmGen(private val program: Program,
} }
internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) {
require(assign.source.expression is PrefixExpression) require(assign.source.expression is PtPrefix)
if(assign.source.datatype==DataType.FLOAT) { 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. // 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) assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
@ -350,9 +342,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.operator in ComparisonOperators) {
if(expr.right.constValue(program)?.number == 0.0) { if(expr.right.asConstInteger() == 0) {
if(expr.operator == "==" || expr.operator=="!=") { if(expr.operator == "==" || expr.operator=="!=") {
when(assign.target.datatype) { when(assign.target.datatype) {
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
@ -365,30 +357,36 @@ internal class AssignmentAsmGen(private val program: Program,
val origTarget = assign.target.origAstTarget val origTarget = assign.target.origAstTarget
if(origTarget!=null) { if(origTarget!=null) {
assignConstantByte(assign.target, 0) assignConstantByte(assign.target, 0)
val assignTrue = AnonymousScope(mutableListOf( val assignTrue = PtNodeGroup()
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position) val assignment = PtAssignment(assign.position)
), assign.position) assignment.add(origTarget)
val assignFalse = AnonymousScope(mutableListOf(), assign.position) assignment.add(PtNumber.fromBoolean(true, assign.position))
val ifelse = IfElse(expr.copy(), assignTrue, assignFalse, assign.position) assignTrue.add(assignment)
ifelse.linkParents(expr) val assignFalse = PtNodeGroup()
val ifelse = PtIfElse(assign.position)
val exprClone = arrayOf(expr).clone()[0]
require(exprClone !== expr) // TODO remove check if it works
ifelse.add(expr)
ifelse.add(assignTrue)
ifelse.add(assignFalse)
asmgen.translate(ifelse) asmgen.translate(ifelse)
return true return true
} }
} }
if(!expr.inferType(program).isInteger) if(expr.type !in IntegerDatatypes)
return false return false
fun simpleLogicalBytesExpr() { fun simpleLogicalBytesExpr() {
// both left and right expression operands are simple. // 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) 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) assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
else { else {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.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.definingSub())
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
when (expr.operator) { when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
@ -402,15 +400,15 @@ internal class AssignmentAsmGen(private val program: Program,
fun simpleLogicalWordsExpr() { fun simpleLogicalWordsExpr() {
// both left and right expression operands are simple. // 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) 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) assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
else { else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, 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.definingSub())
when (expr.operator) { when (expr.operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1") "&", "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") "|", "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.operator in setOf("&", "|", "^", "and", "or", "xor")) {
if (expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes) { if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right.isSimple) { if (expr.right.isSimple()) {
simpleLogicalBytesExpr() simpleLogicalBytesExpr()
return true return true
} }
} }
if (expr.left.inferType(program).isWords && expr.right.inferType(program).isWords) { if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.right.isSimple) { if (expr.right.isSimple()) {
simpleLogicalWordsExpr() simpleLogicalWordsExpr()
return true return true
} }
@ -439,11 +437,11 @@ internal class AssignmentAsmGen(private val program: Program,
if(expr.operator == "==" || expr.operator == "!=") { if(expr.operator == "==" || expr.operator == "!=") {
// expression datatype is BOOL (ubyte) but operands can be anything // expression datatype is BOOL (ubyte) but operands can be anything
if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes && if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes &&
expr.left.isSimple && expr.right.isSimple) { expr.left.isSimple() && expr.right.isSimple()) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.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.definingSub())
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") { if(expr.operator=="==") {
asmgen.out(""" asmgen.out("""
@ -464,12 +462,12 @@ internal class AssignmentAsmGen(private val program: Program,
} }
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
return true return true
} else if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords && } else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
expr.left.isSimple && expr.right.isSimple) { expr.left.isSimple() && expr.right.isSimple()) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, 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.definingSub())
asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") { if(expr.operator=="==") {
@ -499,12 +497,12 @@ internal class AssignmentAsmGen(private val program: Program,
return false return false
} }
else if(expr.operator=="+" || expr.operator=="-") { 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 left = expr.left
val right = expr.right val right = expr.right
if(dt in ByteDatatypes) { if(dt in ByteDatatypes) {
when (right) { when (right) {
is IdentifierReference -> { is PtIdentifier -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
val symname = asmgen.asmVariableName(right) val symname = asmgen.asmVariableName(right)
if(expr.operator=="+") if(expr.operator=="+")
@ -514,7 +512,7 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
return true return true
} }
is NumericLiteral -> { is PtNumber -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
if(expr.operator=="+") if(expr.operator=="+")
asmgen.out(" clc | adc #${right.number.toHex()}") asmgen.out(" clc | adc #${right.number.toHex()}")
@ -527,7 +525,7 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} else if(dt in WordDatatypes) { } else if(dt in WordDatatypes) {
when (right) { when (right) {
is AddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
val symbol = asmgen.asmVariableName(right.identifier) val symbol = asmgen.asmVariableName(right.identifier)
if(expr.operator=="+") if(expr.operator=="+")
@ -551,7 +549,7 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
is IdentifierReference -> { is PtIdentifier -> {
val symname = asmgen.asmVariableName(right) val symname = asmgen.asmVariableName(right)
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+") if(expr.operator=="+")
@ -575,7 +573,7 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
is NumericLiteral -> { is PtNumber -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+") { if(expr.operator=="+") {
asmgen.out(""" asmgen.out("""
@ -599,10 +597,10 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
is TypecastExpression -> { is PtTypeCast -> {
val castedValue = right.expression val castedValue = right.value
if(right.type in WordDatatypes && castedValue.inferType(program).isBytes) { if(right.type in WordDatatypes && castedValue.type in ByteDatatypes) {
if(castedValue is IdentifierReference) { if(castedValue is PtIdentifier) {
val castedSymname = asmgen.asmVariableName(castedValue) val castedSymname = asmgen.asmVariableName(castedValue)
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+") if(expr.operator=="+")
@ -629,11 +627,11 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
else if(expr.operator=="<<" || expr.operator==">>") { else if(expr.operator=="<<" || expr.operator==">>") {
val shifts = expr.right.constValue(program)?.number?.toInt() val shifts = expr.right.asConstInteger()
if(shifts!=null) { if(shifts!=null) {
val dt = expr.left.inferType(program) val dt = expr.left.type
if(dt.isBytes && shifts in 0..7) { if(dt in ByteDatatypes && shifts in 0..7) {
val signed = dt istype DataType.BYTE val signed = dt == DataType.BYTE
assignExpressionToRegister(expr.left, RegisterOrPair.A, signed) assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(expr.operator=="<<") { if(expr.operator=="<<") {
repeat(shifts) { repeat(shifts) {
@ -650,8 +648,8 @@ internal class AssignmentAsmGen(private val program: Program,
} }
assignRegisterByte(assign.target, CpuRegister.A) assignRegisterByte(assign.target, CpuRegister.A)
return true return true
} else if(dt.isWords && shifts in 0..7) { } else if(dt in WordDatatypes && shifts in 0..7) {
val signed = dt istype DataType.WORD val signed = dt == DataType.WORD
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed) assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
if(expr.operator=="<<") { if(expr.operator=="<<") {
if(shifts>0) { if(shifts>0) {
@ -683,11 +681,11 @@ internal class AssignmentAsmGen(private val program: Program,
return false 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) assignExpressionToRegister(left, RegisterOrPair.A, false)
val operand = when(right) { val operand = when(right) {
is NumericLiteral -> "#${right.number.toHex()}" is PtNumber -> "#${right.number.toHex()}"
is IdentifierReference -> asmgen.asmSymbolName(right) is PtIdentifier -> asmgen.asmSymbolName(right)
else -> throw AssemblyError("wrong right operand type") else -> throw AssemblyError("wrong right operand type")
} }
when (operator) { when (operator) {
@ -699,10 +697,10 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterByte(target, CpuRegister.A) 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) assignExpressionToRegister(left, RegisterOrPair.AY, false)
when(right) { when(right) {
is NumericLiteral -> { is PtNumber -> {
val number = right.number.toHex() val number = right.number.toHex()
when (operator) { when (operator) {
"&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla") "&", "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") else -> throw AssemblyError("invalid operator")
} }
} }
is IdentifierReference -> { is PtIdentifier -> {
val name = asmgen.asmSymbolName(right) val name = asmgen.asmSymbolName(right)
when (operator) { when (operator) {
"&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla") "&", "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) assignRegisterpairWord(target, RegisterOrPair.AY)
} }
private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when (expr.operator) { when (expr.operator) {
"==" -> { "==" -> {
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) { when(val dt = expr.left.type) {
in ByteDatatypes -> { in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE) assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(""" 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 -> { in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE) assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1") asmgen.out(" beq + | lda #1")
@ -807,65 +805,20 @@ internal class AssignmentAsmGen(private val program: Program,
assignStackValue(assign.target) assignStackValue(assign.target)
} }
private fun containmentCheckIntoA(containment: ContainmentCheck) { private fun containmentCheckIntoA(containment: PtContainmentCheck) {
val elementDt = containment.element.inferType(program) val elementDt = containment.element.type
val variable = (containment.iterable as? IdentifierReference)?.targetVarDecl(program) val variable = (containment.iterable as? PtIdentifier)?.targetVarDecl(program)
?: throw AssemblyError("invalid containment iterable type") ?: throw AssemblyError("invalid containment iterable type")
if(variable.origin!=VarDeclOrigin.USERCODE) { val varname = asmgen.asmVariableName(containment.iterable)
when(variable.datatype) { when(variable.type) {
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")
}
}
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
when(variable.datatype) {
DataType.STR -> { DataType.STR -> {
// use subroutine // use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSub()!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSub(), "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
val stringVal = variable.value as StringLiteral val stringVal = variable.value as PtString
asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
return return
@ -874,19 +827,19 @@ internal class AssignmentAsmGen(private val program: Program,
throw AssemblyError("containment check of floats not supported") throw AssemblyError("containment check of floats not supported")
} }
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
val numElements = variable.arraysize!!.constIndex()!! val numElements = variable.arraySize!!
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSub()!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSub(), "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
return return
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val numElements = variable.arraysize!!.constIndex()!! val numElements = variable.arraySize!!
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt, containment.definingSub())
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSub(), "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
return return
@ -913,14 +866,13 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterByte(target, CpuRegister.A) assignRegisterByte(target, CpuRegister.A)
} }
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) { private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: PtExpression, origTypeCastExpression: PtTypeCast) {
val valueIDt = value.inferType(program) val valueDt = value.type
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(valueDt==targetDt) if(valueDt==targetDt)
throw AssemblyError("type cast to identical dt should have been removed") throw AssemblyError("type cast to identical dt should have been removed")
when(value) { when(value) {
is IdentifierReference -> { is PtIdentifier -> {
if(targetDt in WordDatatypes) { if(targetDt in WordDatatypes) {
if(valueDt==DataType.UBYTE) { if(valueDt==DataType.UBYTE) {
assignVariableUByteIntoWord(target, value) assignVariableUByteIntoWord(target, value)
@ -932,47 +884,47 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
} }
is DirectMemoryRead -> { is PtMemoryByte -> {
if(targetDt in WordDatatypes) { if(targetDt in WordDatatypes) {
fun assignViaExprEval(addressExpression: Expression) { fun assignViaExprEval(addressExpression: PtExpression) {
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
asmgen.out(" ldy #0") asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
} }
when (value.addressExpression) { when (value.address) {
is NumericLiteral -> { is PtNumber -> {
val address = (value.addressExpression as NumericLiteral).number.toUInt() val address = (value.address as PtNumber).number.toUInt()
assignMemoryByteIntoWord(target, address, null) assignMemoryByteIntoWord(target, address, null)
} }
is IdentifierReference -> { is PtIdentifier -> {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference) assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
} }
is BinaryExpression -> { is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) { if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
asmgen.out(" ldy #0") asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
} else { } else {
assignViaExprEval(value.addressExpression) assignViaExprEval(value.address)
} }
} }
else -> { else -> {
assignViaExprEval(value.addressExpression) assignViaExprEval(value.address)
} }
} }
return 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 -> {} else -> {}
} }
// special case optimizations // special case optimizations
if(target.kind == TargetStorageKind.VARIABLE) { 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) return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
when (valueDt) { when (valueDt) {
@ -998,7 +950,7 @@ internal class AssignmentAsmGen(private val program: Program,
} }
if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) { 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) { if(parentTc!=null && parentTc.type==DataType.UWORD) {
// typecast a word value to ubyte and directly back to uword // typecast a word value to ubyte and directly back to uword
// generate code for lsb(value) here instead of the ubyte typecast // generate code for lsb(value) here instead of the ubyte typecast
@ -1111,25 +1063,25 @@ internal class AssignmentAsmGen(private val program: Program,
when(valueDt) { when(valueDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
assignExpressionToRegister(value, RegisterOrPair.Y, false) assignExpressionToRegister(value, RegisterOrPair.Y, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.out(" jsr floats.FREADUY") asmgen.out(" jsr floats.FREADUY")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.BYTE -> { DataType.BYTE -> {
assignExpressionToRegister(value, RegisterOrPair.A, true) assignExpressionToRegister(value, RegisterOrPair.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.out(" jsr floats.FREADSA") asmgen.out(" jsr floats.FREADSA")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.UWORD -> { DataType.UWORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, false) assignExpressionToRegister(value, RegisterOrPair.AY, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.out(" jsr floats.GIVUAYFAY") asmgen.out(" jsr floats.GIVUAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.WORD -> { DataType.WORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, true) assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.out(" jsr floats.GIVAYFAY") asmgen.out(" jsr floats.GIVAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
@ -1147,9 +1099,9 @@ internal class AssignmentAsmGen(private val program: Program,
asmgen.assignExpressionTo(origTypeCastExpression, target) asmgen.assignExpressionTo(origTypeCastExpression, target)
} }
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) { private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position) val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position)
lsb.linkParents(value.parent) lsb.add(value)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, target, false, program.memsizer, value.position) val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
@ -1175,7 +1127,7 @@ internal class AssignmentAsmGen(private val program: Program,
if(sourceDt == targetDt) if(sourceDt == targetDt)
throw AssemblyError("typecast to identical type") throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression) // also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast)
when(sourceDt) { when(sourceDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
when(targetDt) { when(targetDt) {
@ -1290,7 +1242,7 @@ internal class AssignmentAsmGen(private val program: Program,
if(sourceDt == targetDt) if(sourceDt == targetDt)
throw AssemblyError("typecast to identical type") throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression) // also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast)
when(sourceDt) { when(sourceDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
when(targetDt) { when(targetDt) {
@ -1793,11 +1745,11 @@ internal class AssignmentAsmGen(private val program: Program,
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""") sty P8ZP_SCRATCH_W1+1""")
val constIndex = target.array!!.indexer.constIndex() val constIndex = target.array!!.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
asmgen.out(" lda #$constIndex") asmgen.out(" lda #$constIndex")
} else { } 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(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float_from_fac1") asmgen.out(" jsr floats.set_array_float_from_fac1")
@ -1829,11 +1781,11 @@ internal class AssignmentAsmGen(private val program: Program,
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""") sty P8ZP_SCRATCH_W2+1""")
val constIndex = target.array!!.indexer.constIndex() val constIndex = target.array!!.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
asmgen.out(" lda #$constIndex") asmgen.out(" lda #$constIndex")
} else { } 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(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float") asmgen.out(" jsr floats.set_array_float")
@ -1872,11 +1824,11 @@ internal class AssignmentAsmGen(private val program: Program,
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""") sty P8ZP_SCRATCH_W2+1""")
val constIndex = target.array!!.indexer.constIndex() val constIndex = target.array!!.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
asmgen.out(" lda #$constIndex") asmgen.out(" lda #$constIndex")
} else { } 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(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float") asmgen.out(" jsr floats.set_array_float")
@ -1906,14 +1858,14 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!) storeRegisterAInMemoryAddress(target.memory!!)
} }
TargetStorageKind.ARRAY -> { 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 // assigning an indexed pointer var
if (target.constArrayIndexValue==0u) { if (target.constArrayIndexValue==0u) {
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable)
} else { } else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) 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") asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
} else { } else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
@ -1961,7 +1913,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) val sourceName = asmgen.asmVariableName(bytevar)
when (wordtarget.kind) { when (wordtarget.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -2042,7 +1994,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) val sourceName = asmgen.asmVariableName(bytevar)
when(wordtarget.kind) { when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -2129,7 +2081,7 @@ internal class AssignmentAsmGen(private val program: Program,
CpuRegister.X -> asmgen.out(" txa") CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya") 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") asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
} }
} }
@ -2448,14 +2400,14 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!) storeRegisterAInMemoryAddress(target.memory!!)
} }
TargetStorageKind.ARRAY -> { 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 // assigning an indexed pointer var
if (target.constArrayIndexValue==0u) { if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #0") asmgen.out(" lda #0")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable)
} else { } else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) 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") asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
} else { } else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
@ -2504,14 +2456,14 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!) storeRegisterAInMemoryAddress(target.memory!!)
} }
TargetStorageKind.ARRAY -> { 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 // assigning an indexed pointer var
if (target.constArrayIndexValue==0u) { if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #${byte.toHex()}") asmgen.out(" lda #${byte.toHex()}")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar) asmgen.storeAIntoPointerVar(target.origAstTarget.array!!.variable)
} else { } else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) 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") asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
} else { } else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1") asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
@ -2585,7 +2537,7 @@ internal class AssignmentAsmGen(private val program: Program,
""") """)
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val constIndex = target.array!!.indexer.constIndex() val constIndex = target.array!!.index.asConstInteger()
if (constIndex!=null) { if (constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT) val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
if(asmgen.isTargetCpu(CpuType.CPU65c02)) if(asmgen.isTargetCpu(CpuType.CPU65c02))
@ -2606,7 +2558,7 @@ internal class AssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+$indexValue+4 sta ${target.asmVarname}+$indexValue+4
""") """)
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier)
asmgen.out(""" asmgen.out("""
lda #<${target.asmVarname} lda #<${target.asmVarname}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -2647,7 +2599,7 @@ internal class AssignmentAsmGen(private val program: Program,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val arrayVarName = target.asmVarname val arrayVarName = target.asmVarname
val constIndex = target.array!!.indexer.constIndex() val constIndex = target.array!!.index.asConstInteger()
if (constIndex!=null) { if (constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT) val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
asmgen.out(""" asmgen.out("""
@ -2659,7 +2611,7 @@ internal class AssignmentAsmGen(private val program: Program,
ldy #>($arrayVarName+$indexValue) ldy #>($arrayVarName+$indexValue)
jsr floats.copy_float""") jsr floats.copy_float""")
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference) val asmvarname = asmgen.asmVariableName(target.array.index as PtIdentifier)
asmgen.out(""" asmgen.out("""
lda #<${constFloat} lda #<${constFloat}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -2691,7 +2643,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) { if (address != null) {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -2775,7 +2727,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) { if (address != null) {
when(wordtarget.kind) { when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -2838,13 +2790,13 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) { private fun storeRegisterAInMemoryAddress(memoryAddress: PtMemoryByte) {
val addressExpr = memoryAddress.addressExpression val addressExpr = memoryAddress.address
val addressLv = addressExpr as? NumericLiteral val addressLv = addressExpr as? PtNumber
fun storeViaExprEval() { fun storeViaExprEval() {
when(addressExpr) { when(addressExpr) {
is NumericLiteral, is IdentifierReference -> { is PtNumber, is PtIdentifier -> {
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2") asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
} }
@ -2862,10 +2814,10 @@ internal class AssignmentAsmGen(private val program: Program,
addressLv != null -> { addressLv != null -> {
asmgen.out(" sta ${addressLv.number.toHex()}") asmgen.out(" sta ${addressLv.number.toHex()}")
} }
addressExpr is IdentifierReference -> { addressExpr is PtIdentifier -> {
asmgen.storeAIntoPointerVar(addressExpr) asmgen.storeAIntoPointerVar(addressExpr)
} }
addressExpr is BinaryExpression -> { addressExpr is PtBinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true)) if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
storeViaExprEval() storeViaExprEval()
} }
@ -2873,15 +2825,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 src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) { internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) {
if(expr.inferType(program) istype DataType.FLOAT && dt!=DataType.FLOAT) { if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) {
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr") throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
} else { } else {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)

View File

@ -1,15 +1,12 @@
package prog8.codegen.cpu6502.assignment package prog8.codegen.cpu6502.assignment
import prog8.ast.Program import prog8.code.ast.*
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.VariableAllocator 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 assignmentAsmGen: AssignmentAsmGen,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val allocator: VariableAllocator private val allocator: VariableAllocator
@ -21,7 +18,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
when (val value = assign.source.expression!!) { when (val value = assign.source.expression!!) {
is PrefixExpression -> { is PtPrefix -> {
// A = -A , A = +A, A = ~A, A = not A // A = -A , A = +A, A = ~A, A = not A
when (value.operator) { when (value.operator) {
"+" -> {} "+" -> {}
@ -30,13 +27,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> throw AssemblyError("invalid prefix operator") else -> throw AssemblyError("invalid prefix operator")
} }
} }
is TypecastExpression -> inplaceCast(assign.target, value, assign.position) is PtTypeCast -> inplaceCast(assign.target, value, assign.position)
is BinaryExpression -> inplaceBinary(assign.target, value) is PtBinaryExpression -> inplaceBinary(assign.target, value)
else -> throw AssemblyError("invalid aug assign value type") 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!! val astTarget = target.origAstTarget!!
if (binExpr.left isSameAs astTarget) { if (binExpr.left isSameAs astTarget) {
// A = A <operator> Something // A = A <operator> Something
@ -49,7 +46,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
return inplaceModification(target, binExpr.operator, binExpr.left) return inplaceModification(target, binExpr.operator, binExpr.left)
} }
val leftBinExpr = binExpr.left as? BinaryExpression val leftBinExpr = binExpr.left as? PtBinaryExpression
if (leftBinExpr?.operator == binExpr.operator) { if (leftBinExpr?.operator == binExpr.operator) {
// TODO better optimize the chained asm to avoid intermediate stores/loads? // TODO better optimize the chained asm to avoid intermediate stores/loads?
when { 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) { if (rightBinExpr?.operator == binExpr.operator) {
when { when {
binExpr.left isSameAs astTarget -> { binExpr.left isSameAs astTarget -> {
@ -98,8 +95,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
val leftBinExpr = binExpr.left as? BinaryExpression val leftBinExpr = binExpr.left as? PtBinaryExpression
val rightBinExpr = binExpr.right as? BinaryExpression val rightBinExpr = binExpr.right as? PtBinaryExpression
if(leftBinExpr!=null && rightBinExpr==null) { if(leftBinExpr!=null && rightBinExpr==null) {
if(leftBinExpr.left isSameAs astTarget) { if(leftBinExpr.left isSameAs astTarget) {
// X = (X <oper> Right) <oper> Something // X = (X <oper> Right) <oper> Something
@ -141,26 +138,26 @@ 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. // 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). // 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 // 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 value = if(program.memsizer.memorySize(origValue.type) > program.memsizer.memorySize(target.datatype)) {
val typecast = TypecastExpression(origValue, target.datatype, true, origValue.position) val typecast = PtTypeCast(target.datatype, origValue.position)
typecast.linkParents(origValue.parent) typecast.add(origValue)
typecast typecast
} }
else { else {
origValue origValue
} }
val valueLv = (value as? NumericLiteral)?.number val valueLv = (value as? PtNumber)?.number
val ident = value as? IdentifierReference val ident = value as? PtIdentifier
val memread = value as? DirectMemoryRead val memread = value as? PtMemoryByte
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -170,7 +167,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) 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) 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) 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 if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value) inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
} }
@ -182,7 +179,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) 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) 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) memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
value is TypecastExpression -> { value is PtTypeCast -> {
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator))
return return
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value) inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
@ -194,7 +191,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when { when {
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!) 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!!) 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 if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!) inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
} }
@ -206,27 +203,27 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
val memory = target.memory!! val memory = target.memory!!
when (memory.addressExpression) { when (memory.address) {
is NumericLiteral -> { is PtNumber -> {
val addr = (memory.addressExpression as NumericLiteral).number.toInt() val addr = (memory.address as PtNumber).number.toInt()
// re-use code to assign a variable, instead this time, use a direct memory address // re-use code to assign a variable, instead this time, use a direct memory address
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt()) 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) 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) 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 if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value) inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
} }
else -> 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 -> { is PtIdentifier -> {
val pointer = memory.addressExpression as IdentifierReference val pointer = memory.address as PtIdentifier
when { when {
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt()) valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident) ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
value is TypecastExpression -> { value is PtTypeCast -> {
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_pointer(pointer, operator, value) inplaceModification_byte_value_to_pointer(pointer, operator, value)
} }
@ -235,13 +232,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
else -> { else -> {
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from // 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.definingSub()))
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt()) 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) 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) 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 if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
} }
@ -252,91 +249,89 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
with(target.array!!.indexer) { val indexNum = target.array!!.index as? PtNumber
val indexNum = indexExpr as? NumericLiteral val indexVar = target.array.index as? PtIdentifier
val indexVar = indexExpr as? IdentifierReference when {
when { indexNum!=null -> {
indexNum!=null -> { val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}" when (target.datatype) {
when (target.datatype) { in ByteDatatypes -> {
in ByteDatatypes -> { when {
when { valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
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)
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident) memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread) value is PtTypeCast -> {
value is TypecastExpression -> { if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> 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}")
} }
} in WordDatatypes -> {
indexVar!=null -> { when {
when (target.datatype) { valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
in ByteDatatypes -> { ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
val tgt = memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
AsmAssignTarget.fromRegisters( value is PtTypeCast -> {
RegisterOrPair.A, if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
target.datatype == DataType.BYTE, null, inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
asmgen }
) else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
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}")
} }
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") TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
@ -344,21 +339,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) { if (target.datatype == value.type) {
val childIDt = value.expression.inferType(program) val childDt = value.value.type
val childDt = childIDt.getOrElse { throw AssemblyError("unknown dt") }
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) { 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. // 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.) // (works for integer types, not for float.)
inplaceModification(target, operator, value.expression) inplaceModification(target, operator, value.value)
return true return true
} }
} }
return false 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) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) { when (operator) {
@ -394,7 +388,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.storeAIntoZpPointerVar(sourceName) 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 otherName = asmgen.asmVariableName(value)
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
@ -431,7 +425,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.storeAIntoZpPointerVar(sourceName) 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) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
"+" -> { "+" -> {
@ -499,7 +493,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, // this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow). // because the value is evaluated onto the eval stack (=slow).
when (operator) { when (operator) {
@ -594,7 +588,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) val otherName = asmgen.asmVariableName(ident)
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
@ -762,7 +756,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) { when (operator) {
"+" -> { "+" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
@ -799,7 +793,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) { when (operator) {
"+" -> { "+" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
@ -1111,9 +1105,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) val otherName = asmgen.asmVariableName(ident)
when (val valueDt = ident.targetVarDecl(program)!!.datatype) { when (val valueDt = ident.type) {
in ByteDatatypes -> { in ByteDatatypes -> {
// the other variable is a BYTE type so optimize for that // the other variable is a BYTE type so optimize for that
when (operator) { when (operator) {
@ -1356,12 +1350,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, // this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow). // 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() { fun multiplyVarByWordInAY() {
asmgen.out(""" asmgen.out("""
@ -1410,7 +1402,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""") """)
} }
when (valueDt) { when (val valueDt = value.type) {
in ByteDatatypes -> { in ByteDatatypes -> {
// the other variable is a BYTE type so optimize for that // the other variable is a BYTE type so optimize for that
when (operator) { when (operator) {
@ -1581,7 +1573,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: PtSub) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1) asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
@ -1623,8 +1615,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) { private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: PtIdentifier, scope: PtSub) {
val valueDt = ident.targetVarDecl(program)!!.datatype val valueDt = ident.type
if(valueDt != DataType.FLOAT) if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected") throw AssemblyError("float variable expected")
@ -1682,7 +1674,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegisterLocal(CpuRegister.X) 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: PtSub) {
val constValueName = allocator.getFloatAsmConst(value) val constValueName = allocator.getFloatAsmConst(value)
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
@ -1744,9 +1736,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegisterLocal(CpuRegister.X) 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 outerCastDt = cast.type
val innerCastDt = (cast.expression as? TypecastExpression)?.type val innerCastDt = (cast.value as? PtTypeCast)?.type
if (innerCastDt == null) { if (innerCastDt == null) {
// simple typecast where the value is the target // simple typecast where the value is the target
when (target.datatype) { when (target.datatype) {
@ -1789,8 +1781,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// typecast with nested typecast, that has the target as a value // typecast with nested typecast, that has the target as a value
// calculate singular cast that is required // calculate singular cast that is required
val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt
val value = (cast.expression as TypecastExpression).expression val resultingCast = PtTypeCast(castDt, position)
val resultingCast = TypecastExpression(value, castDt, false, position) resultingCast.add((cast.value as PtTypeCast).value)
inplaceCast(target, resultingCast, position) inplaceCast(target, resultingCast, position)
} }
} }
@ -1808,21 +1800,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
val memory = target.memory!! val memory = target.memory!!
when (memory.addressExpression) { when (memory.address) {
is NumericLiteral -> { is PtNumber -> {
val addr = (memory.addressExpression as NumericLiteral).number.toHex() val addr = (memory.address as PtNumber).number.toHex()
asmgen.out(""" asmgen.out("""
lda $addr lda $addr
eor #255 eor #255
sta $addr""") sta $addr""")
} }
is IdentifierReference -> { is PtIdentifier -> {
val sourceName = asmgen.loadByteFromPointerIntoA(memory.addressExpression as IdentifierReference) val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
asmgen.out(" eor #255") asmgen.out(" eor #255")
asmgen.out(" sta ($sourceName),y") asmgen.out(" sta ($sourceName),y")
} }
else -> { 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(""" asmgen.out("""
ldy #0 ldy #0
lda (P8ZP_SCRATCH_W2),y lda (P8ZP_SCRATCH_W2),y

View File

@ -445,18 +445,13 @@ internal fun asmGeneratorFor(program: Program,
symbolTable: SymbolTable, symbolTable: SymbolTable,
options: CompilationOptions): IAssemblyGenerator options: CompilationOptions): IAssemblyGenerator
{ {
if(options.experimentalCodegen) { val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform()
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform() if(options.experimentalCodegen)
return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors) return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors)
} else { else if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) return prog8.codegen.cpu6502.AsmGen(intermediateAst, symbolTable, options, errors)
// TODO rewrite 6502 codegen on new Intermediary Ast or on new Intermediate Representation else if (options.compTarget.name == VMTarget.NAME)
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors) return VmCodeGen(intermediateAst, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) { else
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform() throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
return VmCodeGen(intermediateAst, symbolTable, options, errors)
}
}
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
} }

View File

@ -147,7 +147,7 @@ class Program(val name: String,
fun makeLabel(postfix: String): String { fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++ generatedLabelSequenceNumber++
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix" return "prog8_label_${generatedLabelSequenceNumber}_$postfix"
} }
fun makeLabel(postfix: String, position: Position): Label { fun makeLabel(postfix: String, position: Position): Label {
@ -160,6 +160,3 @@ class Program(val name: String,
return Jump(null, ident, null, label.position) return Jump(null, ident, null, label.position)
} }
} }
const val generatedLabelPrefix = "prog8_label_"

View File

@ -741,23 +741,6 @@ class Subroutine(override val name: String,
override fun toString() = override fun toString() =
"Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
fun shouldKeepA(): KeepAresult {
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
if(!isAsmSubroutine)
return KeepAresult(saveOnEntry = false, saveOnReturn = false)
// 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 = asmReturnvaluesRegisters.any { it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.AX }
return KeepAresult(false, saveAonReturn)
}
// code to provide the ability to reference asmsub parameters via qualified name: // code to provide the ability to reference asmsub parameters via qualified name:
private val asmParamsDecls = mutableMapOf<String, VarDecl>() private val asmParamsDecls = mutableMapOf<String, VarDecl>()

View File

@ -17,11 +17,9 @@ For 9.0 major changes
- OR.... make all this more generic and use some %segment option to create real segments for 64tass? - OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables) - (need separate step in codegen and IR to write the "golden" variables)
- rewrite 6502 codegen on Pt* ast and symboltable, instead of CompilerAst nodes. (work in codegen-on-new-ast branch) - rewrite 6502 codegen on Pt* ast and symboltable, instead of CompilerAst nodes.
- optimize "dotted string" comments again. - optimize "dotted string" comments again.
- update to kotlin 1.8.0 once it is available in IDEA
Need help with Need help with
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -44,7 +42,7 @@ Compiler:
- ir: peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!) - ir: peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
- ir: add more optimizations in IRPeepholeOptimizer - ir: add more optimizations in IRPeepholeOptimizer
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? - ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer?
- vm: somehow be able to load a label address as value? (VmProgramLoader) - vm: somehow be able to load a label address as value? (VmProgramLoader) this may require storing the program in actual memory bytes though...
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition. - 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
It is documented behavior to now loop 'around' $00 but it's too easy to forget about! It is documented behavior to now loop 'around' $00 but it's too easy to forget about!
Lot of work because of so many special cases in ForLoopsAsmgen..... (vm codegen already behaves like this) Lot of work because of so many special cases in ForLoopsAsmgen..... (vm codegen already behaves like this)
@ -58,14 +56,7 @@ Compiler:
Once new codegen is written that is based on the IR, this point is moot anyway as that will have its own dead code removal. Once new codegen is written that is based on the IR, this point is moot anyway as that will have its own dead code removal.
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) - Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that)
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing - add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
- block "golden" treated specially: every var in here will be allocated in the Golden ram area
- that block can only contain variables.
- the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
- just initialize them yourself in start() if you need a non-zero value
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables)
Libraries: Libraries:
@ -96,6 +87,7 @@ Optimizations:
- when a loopvariable of a forloop isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop - when a loopvariable of a forloop isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
but we have no efficient way right now to see if the body references a variable. but we have no efficient way right now to see if the body references a variable.
- optimize function argument expressions better (use temporary variables to replace non-simple expressions?) - optimize function argument expressions better (use temporary variables to replace non-simple expressions?)
- 6502 codegen optimize array1[index] += / -= array2[index] to not use slow stackeval (attemptAssignOptimizedBinexpr)
STRUCTS again? STRUCTS again?