mirror of https://github.com/irmen/prog8.git
more 6502 codegen on new Pt-AST.
This commit is contained in:
parent
5e8f767642
commit
b2cb125bd4
|
@ -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?
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(""))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -591,5 +591,5 @@ $loopLabel""")
|
|||
range.from,
|
||||
asmgen.asmVariableName(stmt.variable),
|
||||
stmt.variable.type,
|
||||
stmt.definingSub())
|
||||
stmt.definingISub())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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> =
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue