mirror of
https://github.com/irmen/prog8.git
synced 2024-06-10 20:29:33 +00:00
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 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?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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)
|
||||||
|
|
114
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt
Normal file
114
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt
Normal 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 }
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
69
codeGenCpu6502/test/TestBuiltinFunctions.kt
Normal file
69
codeGenCpu6502/test/TestBuiltinFunctions.kt
Normal 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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> =
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user