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 definingBlock() = findParentNode<PtBlock>(this)
fun definingSub() = findParentNode<PtSub>(this) fun definingSub() = findParentNode<PtSub>(this)
fun definingAsmSub() = findParentNode<PtAsmSub>(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 { 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.* import prog8.code.core.*
interface IPtSubroutine sealed interface IPtSubroutine {
val name: String
}
class PtAsmSub( class PtAsmSub(
name: String, name: String,
val address: UInt?, val address: UInt?,
val clobbers: Set<CpuRegister>, val clobbers: Set<CpuRegister>,
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>, 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 retvalRegisters: List<RegisterOrStatusflag>,
val inline: Boolean, val inline: Boolean,
position: Position position: Position
@ -105,6 +107,8 @@ class PtAssignTarget(position: Position) : PtNode(position) {
} }
override fun printProperties() {} 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 val defaultEncoding: Encoding
override fun encodeString(str: String, encoding: Encoding): List<UByte> 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 { interface IStringEncoding {
fun encodeString(str: String, encoding: Encoding): List<UByte> 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 } success = { it }
) )
} }
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String { override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
val decoded = when(encoding) { val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)

View File

@ -208,7 +208,7 @@ object AtasciiEncoding {
return Ok(mapped) 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("")) 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 { return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) { } catch (ce: CharConversionException) {

View File

@ -3,6 +3,7 @@ plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
} }
java { java {
@ -29,6 +30,7 @@ dependencies {
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
} }
sourceSets { sourceSets {
@ -40,6 +42,22 @@ sourceSets {
srcDirs = ["${project.projectDir}/res"] 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 /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
@ -11,5 +12,7 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <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> </component>
</module> </module>

View File

@ -13,10 +13,11 @@ internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen(internal val program: PtProgram, class AsmGen(
internal val symbolTable: SymbolTable, val program: PtProgram,
internal val options: CompilationOptions, internal val symbolTable: SymbolTable,
internal val errors: IErrorReporter internal val options: CompilationOptions,
internal val errors: IErrorReporter
): IAssemblyGenerator { ): IAssemblyGenerator {
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100) 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 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)) { if (isTargetCpu(CpuType.CPU65c02)) {
// just use the cpu's stack for all registers, shorter code // just use the cpu's stack for all registers, shorter code
when (register) { when (register) {
@ -317,6 +318,7 @@ class AsmGen(internal val program: PtProgram,
is PtIncludeBinary -> TODO() is PtIncludeBinary -> TODO()
is PtBreakpoint -> TODO() is PtBreakpoint -> TODO()
is PtVariable -> { /* do nothing; variables are handled elsewhere */ } 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 PtBlock -> throw AssemblyError("block should have been handled elsewhere")
is PtNodeGroup -> TODO() is PtNodeGroup -> TODO()
is PtNop -> {} is PtNop -> {}
@ -420,7 +422,7 @@ class AsmGen(internal val program: PtProgram,
internal fun translateExpression(expression: PtExpression) = internal fun translateExpression(expression: PtExpression) =
expressionsAsmGen.translateExpression(expression) 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) builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall, isExpression: Boolean) = internal fun translateFunctionCall(functionCallExpr: PtFunctionCall, isExpression: Boolean) =
@ -689,7 +691,7 @@ $repeatLabel lda $counterVar
} }
private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String { private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
val scope = stmt.definingSub()!! val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope) val asmInfo = subroutineExtra(scope)
var parent = stmt.parent var parent = stmt.parent
while(parent !is PtProgram) { while(parent !is PtProgram) {
@ -969,7 +971,7 @@ $repeatLabel lda $counterVar
when(pointervar?.targetStatement(program)) { when(pointervar?.targetStatement(program)) {
is PtLabel -> { is PtLabel -> {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda ${asmSymbolName(pointervar!!)},y") out(" lda ${asmSymbolName(pointervar)},y")
return true return true
} }
is PtVariable, null -> { 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. // 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) { if(parameter!=null) {
val sub = parameter.definingSub()!! val sub = parameter.definingAsmSub()!! // TODO or also regular PtSub?
require(sub.isAsmSubroutine) { "push/pop arg passing only supported on asmsubs ${sub.position}" }
val shouldKeepA = sub.asmParameterRegisters.any { it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY } val 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(reg.statusflag!=null) {
if(shouldKeepA) if(shouldKeepA)
out(" sta P8ZP_SCRATCH_REG") out(" sta P8ZP_SCRATCH_REG")
@ -2950,6 +2952,7 @@ $repeatLabel lda $counterVar
else -> throw AssemblyError("can't pop $dt") else -> throw AssemblyError("can't pop $dt")
} }
} }
*/
internal fun pushCpuStack(dt: DataType, value: PtExpression) { internal fun pushCpuStack(dt: DataType, value: PtExpression) {
val signed = value.type.oneOf(DataType.BYTE, DataType.WORD) 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 asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen) { private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
} }
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) { internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null) 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) 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) if(discardResult && resultToStack)
throw AssemblyError("cannot both discard the result AND put it onto stack") throw AssemblyError("cannot both discard the result AND put it onto stack")
val sscope = fcall.definingSub()!! val sscope = fcall.definingISub()!!
when (fcall.name) { when (fcall.name) {
"msb" -> funcMsb(fcall, resultToStack, resultRegister) "msb" -> funcMsb(fcall, resultToStack, resultRegister)
@ -52,13 +52,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
require(fcall.args[0] is PtIdentifier) { 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}" "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" -> { "popw" -> {
require(fcall.args[0] is PtIdentifier) { 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}" "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() "rsave" -> funcRsave()
"rsavex" -> funcRsaveX() "rsavex" -> funcRsaveX()
@ -69,6 +71,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"callrom" -> funcCallRom(fcall) "callrom" -> funcCallRom(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
} }
TODO("return correct function result type")
} }
private fun funcRsave() { private fun funcRsave() {
@ -243,24 +246,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}") asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
} else { } else {
if(arg1.isSimple()) { if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub()) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else { } else {
asmgen.pushCpuStack(DataType.UBYTE, arg1) asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub()) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
} }
} }
} }
else -> { else -> {
if(arg1.isSimple()) { if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub()) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else { } else {
asmgen.pushCpuStack(DataType.UBYTE, arg1) asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingSub()) asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1") asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
} }
} }
@ -289,7 +292,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
else -> { else -> {
if(arg1.isSimple()) { 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.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cpy P8ZP_SCRATCH_W1+1 cpy P8ZP_SCRATCH_W1+1
@ -298,7 +301,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""") +""")
} else { } else {
asmgen.pushCpuStack(DataType.UWORD, arg1) 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.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(""" asmgen.out("""
@ -332,8 +335,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) { private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else { else {
@ -471,9 +474,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) 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.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(""" asmgen.out("""
@ -572,9 +575,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) 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.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(""" 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) 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?) { private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall, scope)
val dt = fcall.args.single().type val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { 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]) outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().type val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
@ -674,8 +677,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: PtSub?) { private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall, scope)
val dt = fcall.args.single().type val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { when (dt) {
@ -709,7 +712,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(""" asmgen.out("""
@ -734,7 +737,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier) val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) { if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingSub()!!) asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val index = (addrExpr.right as PtNumber).number.toHex() val index = (addrExpr.right as PtNumber).number.toHex()
asmgen.out(""" asmgen.out("""
@ -1017,8 +1020,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
""") """)
} }
private fun translateArguments(args: MutableList<PtExpression>, signature: FSignature, scope: PtSub?) { private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val callConv = signature.callConvention(args.map { it.type}) val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type})
fun getSourceForFloat(value: PtExpression): AsmAssignSource { fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) { 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 paramName = it.second.name
val conv = it.first.second val conv = it.first.second
val value = it.first.first val value = it.first.first

View File

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

View File

@ -1,8 +1,7 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.CpuRegister import prog8.code.core.*
import prog8.code.core.RegisterOrPair
import kotlin.math.abs import kotlin.math.abs
// TODO include this in the node class directly? // TODO include this in the node class directly?
@ -89,3 +88,25 @@ internal fun PtAsmSub.shouldKeepA(): KeepAresult {
internal fun PtFunctionCall.targetSubroutine(program: PtProgram): IPtSubroutine? { internal fun PtFunctionCall.targetSubroutine(program: PtProgram): IPtSubroutine? {
TODO() 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, range.from,
asmgen.asmVariableName(stmt.variable), asmgen.asmVariableName(stmt.variable),
stmt.variable.type, 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) if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry) asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingSub()!!) asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
} else } 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]) asmgen.pushCpuStack(callee.parameters[it].first.type, call.args[it])
} }
argOrder.forEach { argOrder.forEach {
val param = callee.parameters[it].first val param = callee.parameters[it]
val targetVar = callee.searchParameter(param.name)!! TODO("pop cpu stack into asmsub param ${param.first.name} ${param.second}")
asmgen.popCpuStack(param.type, targetVar, call.definingSub()) // 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)) if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible") 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 statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type 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 targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array val targetArrayIdx = stmt.target.array
val scope = stmt.definingSub() val scope = stmt.definingISub()
when { when {
targetIdent!=null -> { targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent) val what = asmgen.asmVariableName(targetIdent)

View File

@ -32,7 +32,7 @@ internal class ProgramAndVarsGen(
private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() } private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
internal fun generate() { internal fun generate() {
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } // TODO unused?
header() header()
val allBlocks = program.allBlocks() val allBlocks = program.allBlocks()
@ -278,8 +278,9 @@ internal class ProgramAndVarsGen(
if(sub.inline) { if(sub.inline) {
if(options.optimize) { if(options.optimize) {
if(callGraph.unused(sub)) TODO("check if sub is unused")
return // if(callGraph.unused(sub))
// return
// from an inlined subroutine only the local variables are generated, // from an inlined subroutine only the local variables are generated,
// all other code statements are omitted in the subroutine itself // 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.code.core.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.asConstInteger import prog8.codegen.cpu6502.asConstInteger
import prog8.codegen.cpu6502.returnsWhatWhere
import prog8.codegen.cpu6502.targetSubroutine import prog8.codegen.cpu6502.targetSubroutine
import prog8.codegen.cpu6502.targetVarDecl
internal enum class TargetStorageKind { internal enum class TargetStorageKind {
@ -57,21 +57,21 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
with(assign.target) { with(assign.target) {
when { when {
identifier != null -> { 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) { if (parameter!=null) {
val sub = parameter.definingSubroutine!! val sub = parameter.definingAsmSub()
if (sub.isAsmSubroutine) { if (sub!=null) {
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)] val reg = sub.parameters.single { it.first===parameter }.second
if(reg.statusflag!=null) if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly") throw AssemblyError("can't assign value to processor statusflag directly")
else else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, 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) array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingSub(), memory = memory, origAstTarget = this) memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target") 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 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 PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is PtIdentifier -> { is PtIdentifier -> {
val parameter = value.targetVarDecl(program)?.subroutineParameter val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to ${value.name}") // value.targetVarDecl(program)?.subroutineParameter
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine) if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}") throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value) val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system // special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
@ -160,7 +160,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
} }
is PtFunctionCall -> { is PtFunctionCall -> {
val sub = value.targetSubroutine(program)!! val sub = value.targetSubroutine(program)!!
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
?: throw AssemblyError("can't translate zero return values in assignment") ?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)

View File

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

View File

@ -232,7 +232,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
else -> { else -> {
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from // TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
asmgen.assignExpressionTo(memory.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") asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt()) valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
@ -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.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
@ -1615,7 +1615,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.restoreRegisterLocal(CpuRegister.X) 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 val valueDt = ident.type
if(valueDt != DataType.FLOAT) if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected") throw AssemblyError("float variable expected")
@ -1674,7 +1674,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.restoreRegisterLocal(CpuRegister.X) 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) val constValueName = allocator.getFloatAsmConst(value)
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { 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>) = private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map { parameters.map {
val flattenedName = it.definingSub()!!.scopedName + "." + it.name val flattenedName = it.definingISub()!!.name + "." + it.name
val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt) IRSubroutine.IRParam(flattenedName, orig.dt)
} }

View File

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

View File

@ -1,13 +1,8 @@
package prog8tests package prog8tests
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe 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.code.target.Cx16Target
import prog8.compiler.BuiltinFunctions
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
class TestBuiltinFunctions: FunSpec({ class TestBuiltinFunctions: FunSpec({
@ -24,62 +19,5 @@ class TestBuiltinFunctions: FunSpec({
}""" }"""
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null 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 { fun wrapWithProgram(statements: List<Statement>): Program {
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder) 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")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
program.addModule(module) program.addModule(module)
return program return program

View File

@ -9,11 +9,15 @@ import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.* 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.core.*
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.code.target.c64.C64Zeropage import prog8.code.target.c64.C64Zeropage
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
import prog8.compiler.astprocessing.IntermediateAstMaker
import prog8.compiler.astprocessing.SymbolTableMaker import prog8.compiler.astprocessing.SymbolTableMaker
import prog8tests.helpers.* import prog8tests.helpers.*
@ -73,7 +77,8 @@ class TestAsmGenSymbols: StringSpec({
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u) val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
options.compTarget.machine.zeropage = C64Zeropage(options) options.compTarget.machine.zeropage = C64Zeropage(options)
val st = SymbolTableMaker(program, options).make() 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" { "symbol and variable names from strings" {
@ -92,19 +97,19 @@ class TestAsmGenSymbols: StringSpec({
"symbol and variable names from variable identifiers" { "symbol and variable names from variable identifiers" {
val program = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program) 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.asmSymbolName(localvarIdent) shouldBe "localvar"
asmgen.asmVariableName(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.asmSymbolName(localvarIdentScoped) shouldBe "main.start.localvar"
asmgen.asmVariableName(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.asmSymbolName(scopedVarIdent) shouldBe "var_outside"
asmgen.asmVariableName(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.asmSymbolName(scopedVarIdentScoped) shouldBe "main.var_outside"
asmgen.asmVariableName(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" { "symbol and variable names from label identifiers" {
val program = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program) 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.asmSymbolName(localLabelIdent) shouldBe "locallabel"
asmgen.asmVariableName(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.asmSymbolName(localLabelIdentScoped) shouldBe "main.start.locallabel"
asmgen.asmVariableName(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.asmSymbolName(scopedLabelIdent) shouldBe "label_outside"
asmgen.asmVariableName(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.asmSymbolName(scopedLabelIdentScoped) shouldBe "main.label_outside"
asmgen.asmVariableName(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("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_REG")) shouldBe "P8ZP_SCRATCH_REG"
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_W2")) shouldBe "P8ZP_SCRATCH_W2" asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_W2")) shouldBe "P8ZP_SCRATCH_W2"
val id1 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_REG"), Position.DUMMY) val id1 = PtIdentifier("prog8_lib.P8ZP_SCRATCH_REG", DataType.UBYTE, Position.DUMMY)
id1.linkParents(program.toplevelModule) val id2 = PtIdentifier("prog8_lib.P8ZP_SCRATCH_W2", DataType.UWORD, Position.DUMMY)
val id2 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_W2"), Position.DUMMY)
id2.linkParents(program.toplevelModule)
asmgen.asmSymbolName(id1) shouldBe "P8ZP_SCRATCH_REG" asmgen.asmSymbolName(id1) shouldBe "P8ZP_SCRATCH_REG"
asmgen.asmSymbolName(id2) shouldBe "P8ZP_SCRATCH_W2" asmgen.asmSymbolName(id2) shouldBe "P8ZP_SCRATCH_W2"
} }

View File

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

View File

@ -280,12 +280,19 @@ private fun Prog8ANTLRParser.LabeldefContext.toAst(): Statement =
private fun Prog8ANTLRParser.SubroutineContext.toAst() : Subroutine { private fun Prog8ANTLRParser.SubroutineContext.toAst() : Subroutine {
// non-asm subroutine // non-asm subroutine
val returntype = sub_return_part()?.datatype()?.toAst() val returntype = sub_return_part()?.datatype()?.toAst()
return Subroutine(identifier().text, return Subroutine(
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(), identifier().text,
if(returntype==null) emptyList() else listOf(returntype), sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
statement_block()?.toAst() ?: mutableListOf(), if (returntype == null) emptyList() else listOf(returntype),
false, emptyList(),
toPosition()) emptyList(),
emptySet(),
asmAddress = null,
isAsmSubroutine = false,
inline = false,
statements = statement_block()?.toAst() ?: mutableListOf(),
position = toPosition()
)
} }
private fun Prog8ANTLRParser.Sub_paramsContext.toAst(): List<SubroutineParameter> = 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 var statements: MutableList<Statement>,
override val position: Position) : Statement(), INameScope { 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 lateinit var parent: Node
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine") 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 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 FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FSignature(val name: String, class FSignature(val name: String,
val pure: Boolean, // does it have side effects? val pure: Boolean, // does it have side effects?
val parameters: List<FParam>, val parameters: List<FParam>,
val returnType: DataType?, val returnType: DataType?,
val constExpressionFunc: ConstExpressionCaller? = null) { 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)
}
}
}
}
private val functionSignatures: List<FSignature> = listOf( private val functionSignatures: List<FSignature> = listOf(