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
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
TODO()
}
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
@ -66,6 +70,8 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position)
return false
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) {
companion object {
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
}
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,13 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.code.ast.PtProgram
import prog8.code.core.IMachineDefinition
// 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
@ -129,7 +126,7 @@ private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<S
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)
// 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
}
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
// 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
}
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
val mods = mutableListOf<Modification>()
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 fun getAddressArg(line: String, program: Program): UInt? {
private fun getAddressArg(line: String, program: PtProgram): UInt? {
val loadArg = line.trimStart().substring(3).trim()
return when {
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
@ -450,15 +447,7 @@ private fun getAddressArg(line: String, program: Program): UInt? {
val identMatch = identifierRegex.find(loadArg)
if(identMatch!=null) {
val identifier = identMatch.value
val decl = program.toplevelModule.lookup(identifier.split('.')) as? VarDecl
if(decl!=null) {
when(decl.type){
VarDeclType.VAR -> null
VarDeclType.CONST,
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
}
}
else null
TODO("lookup symbol's value $identifier")
} else null
}
else -> loadArg.substring(1).toUIntOrNull()

View File

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

View File

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

View File

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

View File

@ -1,16 +1,15 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.code.ast.*
import prog8.code.core.*
import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: Program,
internal class ExpressionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen,
private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression:Expression) {
internal fun translateExpression(expression: PtExpression) {
if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
}
@ -21,31 +20,31 @@ internal class ExpressionsAsmGen(private val program: Program,
// the rest of the methods are all PRIVATE
private fun translateExpressionInternal(expression: Expression) {
private fun translateExpressionInternal(expression: PtExpression) {
when(expression) {
is PrefixExpression -> translateExpression(expression)
is BinaryExpression -> translateExpression(expression)
is ArrayIndexedExpression -> translateExpression(expression)
is TypecastExpression -> translateExpression(expression)
is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is NumericLiteral -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
is PtPrefix -> translateExpression(expression)
is PtBinaryExpression -> translateExpression(expression)
is PtArrayIndexer -> translateExpression(expression)
is PtTypeCast -> translateExpression(expression)
is PtAddressOf -> translateExpression(expression)
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is PtNumber -> translateExpression(expression)
is PtIdentifier -> translateExpression(expression)
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> TODO("machine register expression node")
else -> TODO("missing expression asmgen for $expression")
}
}
private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) {
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
val sub = call.target.targetSubroutine(program)!!
val sub = call.targetSubroutine(program)!!
asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call, true)
if(sub.regXasResult()) {
@ -54,7 +53,8 @@ internal class ExpressionsAsmGen(private val program: Program,
}
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) {
// 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) {
@ -133,9 +133,9 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateExpression(typecast: TypecastExpression) {
translateExpressionInternal(typecast.expression)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
private fun translateExpression(typecast: PtTypeCast) {
translateExpressionInternal(typecast.value)
when(typecast.value.type) {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
@ -197,12 +197,12 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateExpression(expr: AddressOf) {
private fun translateExpression(expr: PtAddressOf) {
val name = asmgen.asmVariableName(expr.identifier)
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: NumericLiteral) {
private fun translateExpression(expr: PtNumber) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
@ -220,9 +220,9 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateExpression(expr: IdentifierReference) {
private fun translateExpression(expr: PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
when(expr.inferType(program).getOr(DataType.UNDEFINED)) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
}
@ -239,22 +239,17 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateExpression(expr: BinaryExpression) {
private fun translateExpression(expr: PtBinaryExpression) {
// Uses evalstack to evaluate the given expression.
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
val leftIDt = expr.left.inferType(program)
val rightIDt = expr.right.inferType(program)
if(!leftIDt.isKnown || !rightIDt.isKnown)
throw AssemblyError("can't infer type of both expression operands")
val leftDt = leftIDt.getOrElse { throw AssemblyError("unknown dt") }
val rightDt = rightIDt.getOrElse { throw AssemblyError("unknown dt") }
val leftDt = expr.left.type
val rightDt = expr.right.type
// see if we can apply some optimized routines
when(expr.operator) {
"+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = expr.left.constValue(program)?.number?.toInt()
val rightVal = expr.right.constValue(program)?.number?.toInt()
val leftVal = expr.left.asConstInteger()
val rightVal = expr.right.asConstInteger()
if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(expr.right)
if(rightDt in ByteDatatypes) {
@ -318,7 +313,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
"-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
val rightVal = expr.right.asConstInteger()
if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(expr.left)
@ -352,7 +347,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
">>" -> {
val amount = expr.right.constValue(program)?.number?.toInt()
val amount = expr.right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(expr.left)
when (leftDt) {
@ -423,7 +418,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
"<<" -> {
val amount = expr.right.constValue(program)?.number?.toInt()
val amount = expr.right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(expr.left)
if (leftDt in ByteDatatypes) {
@ -450,13 +445,13 @@ internal class ExpressionsAsmGen(private val program: Program,
}
"*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = expr.left as? IdentifierReference
val rightVar = expr.right as? IdentifierReference
val leftVar = expr.left as? PtIdentifier
val rightVar = expr.right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
return translateSquared(leftVar, leftDt)
}
val value = expr.right.constValue(program)
val value = expr.right as? PtNumber
if(value!=null) {
if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt()
@ -516,7 +511,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
"/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
val rightVal = expr.right.asConstInteger()
if(rightVal!=null && rightVal==2) {
translateExpressionInternal(expr.left)
when (leftDt) {
@ -557,8 +552,8 @@ internal class ExpressionsAsmGen(private val program: Program,
}
in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number
if(rightVal==0.0)
val rightVal = expr.right.asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
@ -584,8 +579,8 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
if(expr.isSimple) {
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
if(expr.isSimple()) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
@ -641,7 +636,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
"<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position))
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
@ -671,7 +666,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
">=" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position))
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
@ -683,7 +678,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateSquared(variable: IdentifierReference, dt: DataType) {
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
val asmVar = asmgen.asmVariableName(variable)
when(dt) {
DataType.BYTE, DataType.UBYTE -> {
@ -699,14 +694,12 @@ internal class ExpressionsAsmGen(private val program: Program,
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PrefixExpression) {
translateExpressionInternal(expr.expression)
val itype = expr.inferType(program)
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
private fun translateExpression(expr: PtPrefix) {
translateExpressionInternal(expr.value)
when(expr.operator) {
"+" -> {}
"-" -> {
when(type) {
when(expr.type) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
@ -714,7 +707,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
"~" -> {
when(type) {
when(expr.type) {
in ByteDatatypes ->
asmgen.out("""
lda P8ESTACK_LO+1,x
@ -729,22 +722,18 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
val elementIDt = arrayExpr.inferType(program)
if(!elementIDt.isKnown)
throw AssemblyError("unknown dt")
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
private fun translateExpression(arrayExpr: PtArrayIndexer) {
val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
if(arrayVarDecl.datatype==DataType.UWORD) {
if(arrayExpr.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
if(arrayExpr.index.type != DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
if(asmgen.isZpVar(arrayExpr.variable)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
@ -754,7 +743,7 @@ internal class ExpressionsAsmGen(private val program: Program,
return
}
val constIndexNum = arrayExpr.indexer.constIndex()
val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
when(elementDt) {
@ -881,7 +870,7 @@ internal class ExpressionsAsmGen(private val program: Program,
}
}
private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) {
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A

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

View File

@ -1,16 +1,6 @@
package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
@ -18,51 +8,57 @@ import prog8.codegen.cpu6502.assignment.AsmAssignment
import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen) {
internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) {
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt, false)
restoreXafterCall(stmt)
// just ignore any result values from the function call.
}
internal fun saveXbeforeCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val sub = stmt.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.name}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
}
}
internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
internal fun restoreXafterCall(stmt: PtFunctionCall) {
val sub = stmt.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.name}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
} else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter
internal fun translateFunctionCall(call: PtFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter
// Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
val subAsmName = asmgen.asmSymbolName(call.target)
val sub: IPtSubroutine = call.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.name}")
val subAsmName = asmgen.asmSymbolName(call.name)
if(sub.isAsmSubroutine) {
if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) {
// inline the subroutine.
@ -70,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
// (this condition has been enforced by an ast check earlier)
asmgen.out(" \t; inlined routine follows: ${sub.name}")
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
asmgen.out(" \t; inlined routine end: ${sub.name}")
} else {
asmgen.out(" jsr $subAsmName")
}
}
else {
else if(sub is PtSub) {
if(sub.inline)
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")
}
else throw AssemblyError("invalid sub type")
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
}
private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) {
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().first), call.args[0])
} else {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
registerArgsViaCpuStackEvaluation(call, sub)
} else {
asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it]
val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param), arg)
argumentViaRegister(sub, IndexedValue(it, param.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
// cannot be assigned to a register easily or risk clobbering other registers.
require(callee.isAsmSubroutine) { "register args only for asm subroutine ${callee.position}" }
if(callee.parameters.isEmpty())
return
// use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].type, call.args[it])
asmgen.pushCpuStack(callee.parameters[it].first.type, call.args[it])
}
argOrder.forEach {
val param = callee.parameters[it]
val param = callee.parameters[it].first
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)
val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.type))
if(!isArgumentTypeCompatible(value.type, parameter.type))
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + parameter.name)
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
val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null)
val paramRegister = if(registerOverride==null) sub.parameters[parameter.index].second else RegisterOrStatusflag(registerOverride, null)
val statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type
if(requiredDt!=valueDt) {
if(valueDt largerThan requiredDt)
if(requiredDt!=value.type) {
if(value.type largerThan requiredDt)
throw AssemblyError("can only convert byte values to word param types")
}
if (statusflag!=null) {
if(requiredDt!=valueDt)
if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required")
if (statusflag == Statusflag.Pc) {
// this param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) {
is NumericLiteral -> {
is PtNumber -> {
val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc")
}
is IdentifierReference -> {
is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value)
asmgen.out("""
pha
@ -202,10 +194,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else {
// via register or register pair
register!!
if(requiredDt largerThan valueDt) {
if(requiredDt largerThan value.type) {
// we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
} else {
val target: AsmAssignTarget =
@ -215,9 +207,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
}
val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) {
val addr = AddressOf(value, Position.DUMMY)
val src = if(value.type in PassByReferenceDatatypes) {
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)

View File

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

View File

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

View File

@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
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 {
val asmName = globalFloatConsts[number]

View File

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

View File

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

View File

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

View File

@ -445,18 +445,13 @@ internal fun asmGeneratorFor(program: Program,
symbolTable: SymbolTable,
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)
} else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
// TODO rewrite 6502 codegen on new Intermediary Ast or on new Intermediate Representation
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) {
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform()
return VmCodeGen(intermediateAst, symbolTable, options, errors)
}
}
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
else if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
return prog8.codegen.cpu6502.AsmGen(intermediateAst, symbolTable, options, errors)
else if (options.compTarget.name == VMTarget.NAME)
return VmCodeGen(intermediateAst, symbolTable, options, errors)
else
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 {
generatedLabelSequenceNumber++
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
return "prog8_label_${generatedLabelSequenceNumber}_$postfix"
}
fun makeLabel(postfix: String, position: Position): Label {
@ -160,6 +160,3 @@ class Program(val name: String,
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() =
"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:
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?
- (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.
- update to kotlin 1.8.0 once it is available in IDEA
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: 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?
- 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.
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)
@ -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.
- 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 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:
@ -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
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?)
- 6502 codegen optimize array1[index] += / -= array2[index] to not use slow stackeval (attemptAssignOptimizedBinexpr)
STRUCTS again?