more 6502 codegen on new Pt-AST.

This commit is contained in:
Irmen de Jong 2023-01-07 15:25:33 +01:00
parent 5e8f767642
commit b2cb125bd4
32 changed files with 389 additions and 268 deletions

View File

@ -36,6 +36,7 @@ sealed class PtNode(val position: Position) {
fun definingBlock() = findParentNode<PtBlock>(this)
fun definingSub() = findParentNode<PtSub>(this)
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
fun definingISub() = findParentNode<IPtSubroutine>(this) // TODO null assert here?
}

View File

@ -45,7 +45,18 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
TODO()
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
}
else -> false
}
}
}

View File

@ -3,14 +3,16 @@ package prog8.code.ast
import prog8.code.core.*
interface IPtSubroutine
sealed interface IPtSubroutine {
val name: String
}
class PtAsmSub(
name: String,
val address: UInt?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
val returnTypes: List<DataType>, // TODO join with register as Pairs ?
val returnTypes: List<DataType>, // TODO join with registers below, as Pairs ?
val retvalRegisters: List<RegisterOrStatusflag>,
val inline: Boolean,
position: Position
@ -105,6 +107,8 @@ class PtAssignTarget(position: Position) : PtNode(position) {
}
override fun printProperties() {}
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
}

View File

@ -7,5 +7,5 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
val defaultEncoding: Encoding
override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
}

View File

@ -10,5 +10,5 @@ enum class Encoding(val prefix: String) {
interface IStringEncoding {
fun encodeString(str: String, encoding: Encoding): List<UByte>
fun decodeString(bytes: List<UByte>, encoding: Encoding): String
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
}

View File

@ -23,7 +23,7 @@ object Encoder: IStringEncoding {
success = { it }
)
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)

View File

@ -208,7 +208,7 @@ object AtasciiEncoding {
return Ok(mapped)
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
}
}

View File

@ -27,7 +27,7 @@ object IsoEncoding {
}
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) {

View File

@ -3,6 +3,7 @@ plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
}
java {
@ -29,6 +30,7 @@ dependencies {
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
sourceSets {
@ -40,6 +42,22 @@ sourceSets {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
// note: there are no unit tests in this module!
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -4,6 +4,7 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
@ -11,5 +12,7 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
</component>
</module>

View File

@ -13,10 +13,11 @@ internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen(internal val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions,
internal val errors: IErrorReporter
class AsmGen(
val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions,
internal val errors: IErrorReporter
): IAssemblyGenerator {
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
@ -197,7 +198,7 @@ class AsmGen(internal val program: PtProgram,
return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly
}
internal fun saveRegisterLocal(register: CpuRegister, scope: PtSub) {
internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) {
if (isTargetCpu(CpuType.CPU65c02)) {
// just use the cpu's stack for all registers, shorter code
when (register) {
@ -317,6 +318,7 @@ class AsmGen(internal val program: PtProgram,
is PtIncludeBinary -> TODO()
is PtBreakpoint -> TODO()
is PtVariable -> { /* do nothing; variables are handled elsewhere */ }
is PtScopeVarsDecls -> { /* do nothing; variables are handled elsewhere */ } // TODO translate PtScopeVarsDecls ? or ignore?
is PtBlock -> throw AssemblyError("block should have been handled elsewhere")
is PtNodeGroup -> TODO()
is PtNop -> {}
@ -420,7 +422,7 @@ class AsmGen(internal val program: PtProgram,
internal fun translateExpression(expression: PtExpression) =
expressionsAsmGen.translateExpression(expression)
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall, isExpression: Boolean) =
@ -689,7 +691,7 @@ $repeatLabel lda $counterVar
}
private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
val scope = stmt.definingSub()!!
val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope)
var parent = stmt.parent
while(parent !is PtProgram) {
@ -969,7 +971,7 @@ $repeatLabel lda $counterVar
when(pointervar?.targetStatement(program)) {
is PtLabel -> {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda ${asmSymbolName(pointervar!!)},y")
out(" lda ${asmSymbolName(pointervar)},y")
return true
}
is PtVariable, null -> {
@ -2852,14 +2854,14 @@ $repeatLabel lda $counterVar
}
}
internal fun popCpuStack(dt: DataType, target: PtVariable, scope: PtSub?) {
/* TODO remove?:
internal fun popCpuStack(dt: DataType, target: PtVariable, scope: IPtSubroutine?) {
// note: because A is pushed first so popped last, saving A is often not required here.
val parameter = target.subroutineParameter
val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to") // target.subroutineParameter
if(parameter!=null) {
val sub = parameter.definingSub()!!
require(sub.isAsmSubroutine) { "push/pop arg passing only supported on asmsubs ${sub.position}" }
val sub = parameter.definingAsmSub()!! // TODO or also regular PtSub?
val shouldKeepA = sub.asmParameterRegisters.any { it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY }
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
val reg: RegisterOrStatusflag = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
if(reg.statusflag!=null) {
if(shouldKeepA)
out(" sta P8ZP_SCRATCH_REG")
@ -2950,6 +2952,7 @@ $repeatLabel lda $counterVar
else -> throw AssemblyError("can't pop $dt")
}
}
*/
internal fun pushCpuStack(dt: DataType, value: PtExpression) {
val signed = value.type.oneOf(DataType.BYTE, DataType.WORD)

View File

@ -0,0 +1,114 @@
package prog8.codegen.cpu6502
import prog8.code.core.*
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
when {
it.reg!=null -> "$index:${it.reg}"
it.variable -> "$index:variable"
else -> "$index:???"
}
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FSignature(val name: String,
val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returnType: DataType?) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(returnType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
null -> ReturnConvention(null, null, false)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
}
}
}
return when {
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
actualParamTypes.size==1 -> {
// one parameter goes via register/registerpair
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
else -> {
// multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
CallConvention(paramConvs, returns)
}
}
}
}
private val functionSignatures: List<FSignature> = listOf(
// this set of function have no return value and operate in-place:
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
// cmp returns a status in the carry flag, but not a proper return value
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
FSignature("abs" , true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow:
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null),
FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null),
FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null),
FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null),
FSignature("rsave" , false, emptyList(), null),
FSignature("rsavex" , false, emptyList(), null),
FSignature("rrestore" , false, emptyList(), null),
FSignature("rrestorex" , false, emptyList(), null),
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
)
val BuiltinFunctions = functionSignatures.associateBy { it.name }

View File

@ -9,22 +9,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
}
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
}
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
if (discardResult && fcall.hasNoSideEffects)
return // can just ignore the whole function call altogether
return null // can just ignore the whole function call altogether
if(discardResult && resultToStack)
throw AssemblyError("cannot both discard the result AND put it onto stack")
val sscope = fcall.definingSub()!!
val sscope = fcall.definingISub()!!
when (fcall.name) {
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
@ -52,13 +52,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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 PtIdentifier).targetVarDecl(program)!!, fcall.definingSub())
TODO("pop cpu stack byte into ${fcall.args[0]}")
// asmgen.popCpuStack(DataType.UBYTE, (fcall.args[0] as PtIdentifier).targetVarDecl(program)!!, fcall.definingISub())
}
"popw" -> {
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 PtIdentifier).targetVarDecl(program)!!, fcall.definingSub())
TODO("pop cpu stack word into ${fcall.args[0]}")
// asmgen.popCpuStack(DataType.UWORD, (fcall.args[0] as PtIdentifier).targetVarDecl(program)!!, fcall.definingISub())
}
"rsave" -> funcRsave()
"rsavex" -> funcRsaveX()
@ -69,6 +71,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"callrom" -> funcCallRom(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
}
TODO("return correct function result type")
}
private fun funcRsave() {
@ -243,24 +246,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
} else {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
}
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
}
@ -289,7 +292,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
@ -298,7 +301,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""")
} else {
asmgen.pushCpuStack(DataType.UWORD, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingSub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
@ -332,8 +335,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign)
}
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope)
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else {
@ -471,9 +474,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
@ -572,9 +575,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
@ -628,8 +631,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
}
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope)
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
@ -653,7 +656,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().type
if(resultToStack) {
@ -674,8 +677,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) {
translateArguments(fcall.args, func, scope)
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
@ -709,7 +712,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out("""
@ -734,7 +737,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val index = (addrExpr.right as PtNumber).number.toHex()
asmgen.out("""
@ -1017,8 +1020,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
""")
}
private fun translateArguments(args: MutableList<PtExpression>, signature: FSignature, scope: PtSub?) {
val callConv = signature.callConvention(args.map { it.type})
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type})
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) {
@ -1044,7 +1048,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
args.zip(callConv.params).zip(signature.parameters).forEach {
call.args.zip(callConv.params).zip(signature.parameters).forEach {
val paramName = it.second.name
val conv = it.first.second
val value = it.first.first

View File

@ -53,8 +53,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
asmgen.restoreXafterCall(call)
sub.
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) // TODO does regular sub also have asmReturnvaluesRegisters set?
val returns: List<Pair<DataType, RegisterOrStatusflag>> = sub.returnsWhatWhere()
for ((_, reg) in returns) {
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
if (reg.registerOrPair != null) {

View File

@ -1,8 +1,7 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.CpuRegister
import prog8.code.core.RegisterOrPair
import prog8.code.core.*
import kotlin.math.abs
// TODO include this in the node class directly?
@ -89,3 +88,25 @@ internal fun PtAsmSub.shouldKeepA(): KeepAresult {
internal fun PtFunctionCall.targetSubroutine(program: PtProgram): IPtSubroutine? {
TODO()
}
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<DataType, RegisterOrStatusflag>> {
when(this) {
is PtAsmSub -> {
return returnTypes.zip(this.retvalRegisters)
}
is PtSub -> {
// for non-asm subroutines, determine the return registers based on the type of the return value
return if(returntype==null)
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
listOf(Pair(returntype!!, register))
}
}
}
}

View File

@ -591,5 +591,5 @@ $loopLabel""")
range.from,
asmgen.asmVariableName(stmt.variable),
stmt.variable.type,
stmt.definingSub())
stmt.definingISub())
}

View File

@ -25,9 +25,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
}
}
@ -130,9 +130,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
asmgen.pushCpuStack(callee.parameters[it].first.type, call.args[it])
}
argOrder.forEach {
val param = callee.parameters[it].first
val targetVar = callee.searchParameter(param.name)!!
asmgen.popCpuStack(param.type, targetVar, call.definingSub())
val param = callee.parameters[it]
TODO("pop cpu stack into asmsub param ${param.first.name} ${param.second}")
// val targetVar = callee.searchParameter(param.name)!!
// asmgen.popCpuStack(param.type, targetVar, call.definingISub())
}
}
@ -150,7 +151,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
val paramRegister = if(registerOverride==null) sub.parameters[parameter.index].second else RegisterOrStatusflag(registerOverride, null)
val paramRegister: RegisterOrStatusflag = when(sub) {
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].second else RegisterOrStatusflag(registerOverride, null)
is PtSub -> RegisterOrStatusflag(registerOverride!!, null)
}
val statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type

View File

@ -13,7 +13,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array
val scope = stmt.definingSub()
val scope = stmt.definingISub()
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)

View File

@ -32,7 +32,7 @@ internal class ProgramAndVarsGen(
private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
internal fun generate() {
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } // TODO unused?
header()
val allBlocks = program.allBlocks()
@ -278,8 +278,9 @@ internal class ProgramAndVarsGen(
if(sub.inline) {
if(options.optimize) {
if(callGraph.unused(sub))
return
TODO("check if sub is unused")
// if(callGraph.unused(sub))
// return
// from an inlined subroutine only the local variables are generated,
// all other code statements are omitted in the subroutine itself

View File

@ -4,8 +4,8 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.asConstInteger
import prog8.codegen.cpu6502.returnsWhatWhere
import prog8.codegen.cpu6502.targetSubroutine
import prog8.codegen.cpu6502.targetVarDecl
internal enum class TargetStorageKind {
@ -57,21 +57,21 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
with(assign.target) {
when {
identifier != null -> {
val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter
val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to ${assign.target.identifier!!.name}") // identifier!!.targetVarDecl(program)?.subroutineParameter
if (parameter!=null) {
val sub = parameter.definingSubroutine!!
if (sub.isAsmSubroutine) {
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
val sub = parameter.definingAsmSub()
if (sub!=null) {
val reg = sub.parameters.single { it.first===parameter }.second
if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly")
else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingSub(), register=reg.registerOrPair, origAstTarget = this)
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this)
}
}
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingSub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingISub(), variableAsmName = asmgen.asmVariableName(identifier!!), 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)
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target")
}
}
@ -136,8 +136,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
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)
val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to ${value.name}") // value.targetVarDecl(program)?.subroutineParameter
if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
@ -160,7 +160,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
}
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
val returnType = sub.returnsWhatWhere().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)

View File

@ -181,8 +181,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val sub = value.targetSubroutine(program)!!
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value, true)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
val returnValue = sub.returnsWhatWhere().singleOrNull() { it.second.registerOrPair!=null } ?: sub.returnsWhatWhere().single() { it.second.statusflag!=null }
when (returnValue.first) {
DataType.STR -> {
asmgen.restoreXafterCall(value)
@ -241,10 +240,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
is PtBuiltinFunctionCall -> {
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
val returnDt = asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
if(assign.target.register==null) {
// still need to assign the result to the target variable/etc.
when(builtinFunctionReturnType(value.name)) {
when(returnDt) {
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
DataType.STR -> {
@ -386,7 +385,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
asmgen.restoreRegisterStack(CpuRegister.A, false)
when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
@ -408,7 +407,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
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.definingSub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
when (expr.operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
@ -441,7 +440,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
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.definingSub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
asmgen.out("""
@ -467,7 +466,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
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.definingSub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
@ -815,8 +814,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.STR -> {
// use subroutine
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.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A)
val stringVal = variable.value as PtString
asmgen.out(" ldy #${stringVal.value.length}")
@ -829,8 +828,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB -> {
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.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -838,8 +837,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
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)
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt, containment.definingISub())
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray")
return
@ -1063,25 +1062,25 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(valueDt) {
DataType.UBYTE -> {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADUY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.BYTE -> {
assignExpressionToRegister(value, RegisterOrPair.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADSA")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.UWORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVUAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.WORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingSub()!!)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}

View File

@ -232,7 +232,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
else -> {
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingSub()))
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingISub()))
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
when {
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
@ -1573,7 +1573,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: PtSub) {
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: IPtSubroutine) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
@ -1615,7 +1615,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: PtIdentifier, scope: PtSub) {
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: PtIdentifier, scope: IPtSubroutine) {
val valueDt = ident.type
if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected")
@ -1674,7 +1674,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: PtSub) {
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: IPtSubroutine) {
val constValueName = allocator.getFloatAsmConst(value)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {

View File

@ -0,0 +1,69 @@
package prog8tests.codegencpu6502
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.core.DataType
import prog8.code.core.NumericDatatypesNoBool
import prog8.code.core.RegisterOrPair
import prog8.codegen.cpu6502.BuiltinFunctions
class TestBuiltinFunctions: FunSpec({
test("pure func with fixed type") {
val func = BuiltinFunctions.getValue("sgn")
func.name shouldBe "sgn"
func.parameters.size shouldBe 1
func.parameters[0].name shouldBe "value"
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
func.pure shouldBe true
func.returnType shouldBe DataType.BYTE
val conv = func.callConvention(listOf(DataType.UBYTE))
conv.params.size shouldBe 1
conv.params[0].dt shouldBe DataType.UBYTE
conv.params[0].reg shouldBe RegisterOrPair.A
conv.params[0].variable shouldBe false
conv.returns.dt shouldBe DataType.BYTE
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe RegisterOrPair.A
}
test("not-pure func with varying result value type") {
val func = BuiltinFunctions.getValue("cmp")
func.name shouldBe "cmp"
func.parameters.size shouldBe 2
func.pure shouldBe false
func.returnType shouldBe null
val conv = func.callConvention(listOf(DataType.UWORD, DataType.UWORD))
conv.params.size shouldBe 2
conv.returns.dt shouldBe null
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe null
}
test("func without return type") {
val func = BuiltinFunctions.getValue("poke")
func.name shouldBe "poke"
func.parameters.size shouldBe 2
func.parameters[0].name shouldBe "address"
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
func.parameters[1].name shouldBe "value"
func.parameters[1].possibleDatatypes shouldBe arrayOf(DataType.UBYTE)
func.pure shouldBe false
func.returnType shouldBe null
val conv = func.callConvention(listOf(DataType.UWORD, DataType.UBYTE))
conv.params.size shouldBe 2
conv.params[0].dt shouldBe DataType.UWORD
conv.params[0].reg shouldBe null
conv.params[0].variable shouldBe true
conv.params[1].dt shouldBe DataType.UBYTE
conv.params[1].reg shouldBe null
conv.params[1].variable shouldBe true
conv.returns.dt shouldBe null
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe null
}
})

View File

@ -1244,7 +1244,7 @@ class IRCodeGen(
private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map {
val flattenedName = it.definingSub()!!.scopedName + "." + it.name
val flattenedName = it.definingISub()!!.name + "." + it.name
val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt)
}

View File

@ -14,7 +14,7 @@ internal object DummyStringEncoder : IStringEncoding {
return emptyList()
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
return ""
}
}

View File

@ -1,13 +1,8 @@
package prog8tests
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.code.core.DataType
import prog8.code.core.NumericDatatypesNoBool
import prog8.code.core.RegisterOrPair
import prog8.code.target.Cx16Target
import prog8.compiler.BuiltinFunctions
import prog8tests.helpers.compileText
class TestBuiltinFunctions: FunSpec({
@ -24,62 +19,5 @@ class TestBuiltinFunctions: FunSpec({
}"""
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
}
test("pure func with fixed type") {
val func = BuiltinFunctions.getValue("sgn")
func.name shouldBe "sgn"
func.parameters.size shouldBe 1
func.parameters[0].name shouldBe "value"
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
func.pure shouldBe true
func.returnType shouldBe DataType.BYTE
val conv = func.callConvention(listOf(DataType.UBYTE))
conv.params.size shouldBe 1
conv.params[0].dt shouldBe DataType.UBYTE
conv.params[0].reg shouldBe RegisterOrPair.A
conv.params[0].variable shouldBe false
conv.returns.dt shouldBe DataType.BYTE
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe RegisterOrPair.A
}
test("not-pure func with varying result value type") {
val func = BuiltinFunctions.getValue("cmp")
func.name shouldBe "cmp"
func.parameters.size shouldBe 2
func.pure shouldBe false
func.returnType shouldBe null
val conv = func.callConvention(listOf(DataType.UWORD, DataType.UWORD))
conv.params.size shouldBe 2
conv.returns.dt shouldBe null
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe null
}
test("func without return type") {
val func = BuiltinFunctions.getValue("poke")
func.name shouldBe "poke"
func.parameters.size shouldBe 2
func.parameters[0].name shouldBe "address"
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
func.parameters[1].name shouldBe "value"
func.parameters[1].possibleDatatypes shouldBe arrayOf(DataType.UBYTE)
func.pure shouldBe false
func.returnType shouldBe null
val conv = func.callConvention(listOf(DataType.UWORD, DataType.UBYTE))
conv.params.size shouldBe 2
conv.params[0].dt shouldBe DataType.UWORD
conv.params[0].reg shouldBe null
conv.params[0].variable shouldBe true
conv.params[1].dt shouldBe DataType.UBYTE
conv.params[1].reg shouldBe null
conv.params[1].variable shouldBe true
conv.returns.dt shouldBe null
conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe null
}
})

View File

@ -28,7 +28,7 @@ class TestMemory: FunSpec({
fun wrapWithProgram(statements: List<Statement>): Program {
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), statements.toMutableList(), false, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements.toMutableList(), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
program.addModule(module)
return program

View File

@ -9,11 +9,15 @@ import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.*
import prog8.code.ast.PtAddressOf
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtIdentifier
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8.code.target.c64.C64Zeropage
import prog8.codegen.cpu6502.AsmGen
import prog8.compiler.astprocessing.IntermediateAstMaker
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8tests.helpers.*
@ -73,7 +77,8 @@ class TestAsmGenSymbols: StringSpec({
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
options.compTarget.machine.zeropage = C64Zeropage(options)
val st = SymbolTableMaker(program, options).make()
return AsmGen(program, st, options, errors)
val ptProgram = IntermediateAstMaker(program, st, options).transform()
return AsmGen(ptProgram, st, options, errors)
}
"symbol and variable names from strings" {
@ -92,19 +97,19 @@ class TestAsmGenSymbols: StringSpec({
"symbol and variable names from variable identifiers" {
val program = createTestProgram()
val asmgen = createTestAsmGen(program)
val sub = program.entrypoint
val sub = asmgen.program.entrypoint()!!
val localvarIdent = sub.statements.asSequence().filterIsInstance<Assignment>().first { it.value is IdentifierReference }.value as IdentifierReference
val localvarIdent = sub.children.asSequence().filterIsInstance<PtAssignment>().first { it.value is PtIdentifier }.value as PtIdentifier
asmgen.asmSymbolName(localvarIdent) shouldBe "localvar"
asmgen.asmVariableName(localvarIdent) shouldBe "localvar"
val localvarIdentScoped = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "start", "localvar") }.value as AddressOf).identifier
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(localvarIdentScoped) shouldBe "main.start.localvar"
asmgen.asmVariableName(localvarIdentScoped) shouldBe "main.start.localvar"
val scopedVarIdent = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("var_outside") }.value as AddressOf).identifier
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="var_outside" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(scopedVarIdent) shouldBe "var_outside"
asmgen.asmVariableName(scopedVarIdent) shouldBe "var_outside"
val scopedVarIdentScoped = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "var_outside") }.value as AddressOf).identifier
val scopedVarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(scopedVarIdentScoped) shouldBe "main.var_outside"
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
}
@ -112,19 +117,19 @@ class TestAsmGenSymbols: StringSpec({
"symbol and variable names from label identifiers" {
val program = createTestProgram()
val asmgen = createTestAsmGen(program)
val sub = program.entrypoint
val sub = asmgen.program.entrypoint()!!
val localLabelIdent = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("locallabel") }.value as AddressOf).identifier
val localLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="locallabel" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(localLabelIdent) shouldBe "locallabel"
asmgen.asmVariableName(localLabelIdent) shouldBe "locallabel"
val localLabelIdentScoped = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","start","locallabel") }.value as AddressOf).identifier
val localLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(localLabelIdentScoped) shouldBe "main.start.locallabel"
asmgen.asmVariableName(localLabelIdentScoped) shouldBe "main.start.locallabel"
val scopedLabelIdent = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("label_outside") }.value as AddressOf).identifier
val scopedLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="label_outside" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(scopedLabelIdent) shouldBe "label_outside"
asmgen.asmVariableName(scopedLabelIdent) shouldBe "label_outside"
val scopedLabelIdentScoped = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","label_outside") }.value as AddressOf).identifier
val scopedLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier
asmgen.asmSymbolName(scopedLabelIdentScoped) shouldBe "main.label_outside"
asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside"
}
@ -145,10 +150,8 @@ main {
asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2"
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG"
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_W2")) shouldBe "P8ZP_SCRATCH_W2"
val id1 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_REG"), Position.DUMMY)
id1.linkParents(program.toplevelModule)
val id2 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_W2"), Position.DUMMY)
id2.linkParents(program.toplevelModule)
val id1 = PtIdentifier("prog8_lib.P8ZP_SCRATCH_REG", DataType.UBYTE, Position.DUMMY)
val id2 = PtIdentifier("prog8_lib.P8ZP_SCRATCH_W2", DataType.UWORD, Position.DUMMY)
asmgen.asmSymbolName(id1) shouldBe "P8ZP_SCRATCH_REG"
asmgen.asmSymbolName(id2) shouldBe "P8ZP_SCRATCH_W2"
}

View File

@ -29,7 +29,7 @@ internal object DummyStringEncoder : IStringEncoding {
return emptyList()
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
return ""
}
}
@ -37,7 +37,7 @@ internal object DummyStringEncoder : IStringEncoding {
internal object AsciiStringEncoder : IStringEncoding {
override fun encodeString(str: String, encoding: Encoding): List<UByte> = str.map { it.code.toUByte() }
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
return bytes.joinToString()
}
}
@ -53,7 +53,7 @@ internal object DummyCompilationTarget : ICompilationTarget {
throw NotImplementedError("dummy")
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
throw NotImplementedError("dummy")
}

View File

@ -280,12 +280,19 @@ private fun Prog8ANTLRParser.LabeldefContext.toAst(): Statement =
private fun Prog8ANTLRParser.SubroutineContext.toAst() : Subroutine {
// non-asm subroutine
val returntype = sub_return_part()?.datatype()?.toAst()
return Subroutine(identifier().text,
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
if(returntype==null) emptyList() else listOf(returntype),
statement_block()?.toAst() ?: mutableListOf(),
false,
toPosition())
return Subroutine(
identifier().text,
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
if (returntype == null) emptyList() else listOf(returntype),
emptyList(),
emptyList(),
emptySet(),
asmAddress = null,
isAsmSubroutine = false,
inline = false,
statements = statement_block()?.toAst() ?: mutableListOf(),
position = toPosition()
)
}
private fun Prog8ANTLRParser.Sub_paramsContext.toAst(): List<SubroutineParameter> =

View File

@ -695,22 +695,6 @@ class Subroutine(override val name: String,
override var statements: MutableList<Statement>,
override val position: Position) : Statement(), INameScope {
constructor(name: String, parameters: MutableList<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
companion object {
private fun determineReturnRegisters(returntypes: List<DataType>): List<RegisterOrStatusflag> {
// for non-asm subroutines, determine the return registers based on the type of the return value
return when(returntypes.singleOrNull()) {
in ByteDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null))
in WordDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
DataType.FLOAT -> listOf(RegisterOrStatusflag(RegisterOrPair.FAC1, null))
null -> emptyList()
else -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
}
}
}
override lateinit var parent: Node
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine")

View File

@ -14,75 +14,13 @@ import kotlin.math.sqrt
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
when {
it.reg!=null -> "$index:${it.reg}"
it.variable -> "$index:variable"
else -> "$index:???"
}
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FSignature(val name: String,
val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returnType: DataType?,
val constExpressionFunc: ConstExpressionCaller? = null) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(returnType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
null -> ReturnConvention(null, null, false)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
}
}
}
return when {
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
actualParamTypes.size==1 -> {
// one parameter goes via register/registerpair
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
else -> {
// multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
CallConvention(paramConvs, returns)
}
}
}
}
val constExpressionFunc: ConstExpressionCaller? = null)
private val functionSignatures: List<FSignature> = listOf(