mirror of
https://github.com/irmen/prog8.git
synced 2025-09-12 00:24:34 +00:00
Compare commits
1 Commits
v12.0-beta
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
|
357be82446 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,13 +7,10 @@ build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
|
1290
.idea/inspectionProfiles/Project_Default.xml
generated
1290
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
20
.idea/libraries/KotlinJavaRuntime.xml
generated
20
.idea/libraries/KotlinJavaRuntime.xml
generated
@@ -1,23 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
14
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
14
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
@@ -1,19 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
|
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -26,7 +26,4 @@
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@@ -15,6 +15,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
|
@@ -61,7 +61,6 @@ What does Prog8 provide?
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.2.20"
|
||||
kotlin("jvm") version "2.2.0"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -21,7 +20,6 @@ allprojects {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
// languageVersion.set(KotlinVersion.KOTLIN_2_3)
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -5,7 +7,7 @@ plugins {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -96,13 +96,12 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"prog8_lib_structalloc" to FSignature(true, BaseDataType.UWORD),
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
@@ -132,15 +131,10 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
|
||||
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
|
||||
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"rsave" to FSignature(false, null),
|
||||
|
@@ -13,9 +13,6 @@ enum class BaseDataType {
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
@@ -29,7 +26,6 @@ enum class BaseDataType {
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
@@ -38,8 +34,7 @@ enum class BaseDataType {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this.isWord -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
@@ -55,163 +50,84 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
|
||||
|
||||
interface ISubType {
|
||||
val scopedNameString: String
|
||||
fun memsize(sizer: IMemSizer): Int
|
||||
fun sameas(other: ISubType): Boolean
|
||||
fun getFieldType(name: String): DataType?
|
||||
}
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
|
||||
init {
|
||||
when {
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
else -> {
|
||||
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
|
||||
"sub and subtype can't both be set"
|
||||
}
|
||||
}
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
|
||||
return base == other.base && sub == other.sub
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType): DataType {
|
||||
if(dt.isStructInstance)
|
||||
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
|
||||
return simpletypes.getValue(dt)
|
||||
}
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
else
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
|
||||
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
|
||||
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
fun pointer(dt: DataType): DataType = if(dt.isBasic)
|
||||
DataType(BaseDataType.POINTER, dt.base, null)
|
||||
else
|
||||
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
|
||||
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
|
||||
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
|
||||
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
|
||||
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
|
||||
}
|
||||
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
if (isBasic)
|
||||
return pointer(base)
|
||||
if (isString)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
if (isPointer)
|
||||
return UWORD
|
||||
if (isArray) {
|
||||
if (msb || isSplitWordArray)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
val elementDt = elementType()
|
||||
require(elementDt.isBasic)
|
||||
return pointer(elementDt)
|
||||
}
|
||||
if (subType != null)
|
||||
return pointer(this)
|
||||
return UWORD
|
||||
}
|
||||
}
|
||||
|
||||
fun dereference(): DataType {
|
||||
require(isPointer || isUnsignedWord)
|
||||
return when {
|
||||
isUnsignedWord -> forDt(BaseDataType.UBYTE)
|
||||
sub!=null -> forDt(sub)
|
||||
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
|
||||
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
|
||||
else -> throw IllegalArgumentException("cannot dereference this pointer type")
|
||||
}
|
||||
}
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
@@ -232,15 +148,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
@@ -253,30 +160,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}"
|
||||
subType!=null -> "^^${subType!!.scopedNameString}"
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
when {
|
||||
sub!=null -> sub.name.lowercase()
|
||||
subType!=null -> subType!!.scopedNameString
|
||||
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}["
|
||||
subType!=null -> "^^${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
@@ -304,37 +187,18 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord || this == targetType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
|
||||
BaseDataType.ARRAY_POINTER -> false
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
// note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to
|
||||
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
|
||||
memsizer.memorySize(sub)
|
||||
} else if(subType!=null) {
|
||||
subType!!.memsize(memsizer)
|
||||
} else {
|
||||
memsizer.memorySize(base)
|
||||
}
|
||||
|
||||
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
@@ -350,27 +214,22 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isPointer = base.isPointer
|
||||
val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
|
||||
val isPointerToWord = base.isPointer && sub?.isWord==true
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
@@ -421,13 +280,10 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
|
@@ -23,9 +23,8 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
|
||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word
|
||||
abstract val SCRATCH_PTR : UInt // temp storage for a pointer
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||
|
||||
|
||||
// the variables allocated into Zeropage.
|
||||
@@ -39,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +72,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString || datatype.isArray -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
@@ -124,7 +122,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
datatype.isString -> VarAllocation(address, datatype, size)
|
||||
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||
datatype.isPointer -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
@@ -31,10 +31,10 @@ class C128Target: ICompilationTarget,
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO address?
|
||||
override val BSSHIGHRAM_END = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_START = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_END = 0u // TODO address?
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
@@ -9,7 +9,7 @@ import java.nio.file.Path
|
||||
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
@@ -33,7 +33,6 @@ class ConfigFileTarget(
|
||||
val zpScratchReg: UInt,
|
||||
val zpScratchW1: UInt,
|
||||
val zpScratchW2: UInt,
|
||||
val zpScratchPtr: UInt,
|
||||
val virtualregistersStart: UInt,
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
@@ -133,7 +132,6 @@ class ConfigFileTarget(
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
props.getInteger("zp_scratch_w1"),
|
||||
props.getInteger("zp_scratch_w2"),
|
||||
props.getInteger("zp_scratch_ptr"),
|
||||
props.getInteger("virtual_registers"),
|
||||
zpFullsafe,
|
||||
zpKernalsafe,
|
||||
@@ -161,7 +159,7 @@ class ConfigFileTarget(
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = ConfigurableZeropage(
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||
virtualregistersStart,
|
||||
zpBasicsafe,
|
||||
zpKernalsafe,
|
||||
|
@@ -8,7 +8,7 @@ import java.nio.file.Path
|
||||
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
@@ -18,7 +18,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
@@ -7,9 +7,7 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
@@ -28,8 +26,6 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isStructInstance -> dt.subType!!.memsize(this)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import java.nio.file.Path
|
||||
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
@@ -104,6 +104,4 @@ private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_PTR: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x0bu // temp storage for a pointer $0b+$0c
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
|
@@ -9,7 +9,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x9bu // temp storage for a pointer $9b+$9c
|
||||
|
||||
|
||||
init {
|
||||
@@ -22,6 +21,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
|
@@ -9,7 +9,6 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||
override val SCRATCH_PTR = 0x22u // temp storage for a pointer $22+$23
|
||||
|
||||
|
||||
init {
|
||||
|
@@ -10,8 +10,6 @@ class ConfigurableZeropage(
|
||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||
override val SCRATCH_PTR: UInt, // temp storage for a pointer
|
||||
|
||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||
basicsafe: List<UIntRange>,
|
||||
kernalsafe: List<UIntRange>,
|
||||
|
@@ -14,7 +14,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
override val SCRATCH_PTR = 0xb1u // temp storage for a pointer $b1+$b2
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
@@ -35,7 +34,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb1u..0xbau) // TODO more?
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -7,10 +9,12 @@ dependencies {
|
||||
implementation(project(":simpleAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -13,6 +13,7 @@
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<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" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
|
@@ -30,17 +30,14 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
private fun prefixSymbols(program: PtProgram, options: CompilationOptions, st: SymbolTable): SymbolTable {
|
||||
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
|
||||
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
|
||||
val expressionsToFixSubtype = mutableListOf<PtExpression>()
|
||||
val variablesToFixSubtype = mutableListOf<IPtVariable>()
|
||||
|
||||
fun prefixNamedNode(node: PtNamedNode) {
|
||||
when(node) {
|
||||
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
|
||||
is PtBlock -> node.name = "p8b_${node.name}"
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // only prefix user defined labels
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
is PtStructDecl -> node.name = "p8t_${node.name}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +50,10 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
|
||||
}
|
||||
}
|
||||
is PtSub -> prefixNamedNode(node)
|
||||
is PtSub -> {
|
||||
prefixNamedNode(node)
|
||||
node.parameters.forEach { prefixNamedNode(it) }
|
||||
}
|
||||
is PtFunctionCall -> {
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
@@ -62,28 +62,11 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
// check if the identifier is part of a pointer dereference (which means you cannot look it up in the symboltable because it's a struct field)
|
||||
val pexpr = node.parent as? PtBinaryExpression
|
||||
if(pexpr?.operator==".") {
|
||||
if(pexpr.left is PtArrayIndexer) {
|
||||
val arrayvar = (pexpr.left as PtArrayIndexer).variable!!
|
||||
if(arrayvar.type.subType!=null) {
|
||||
// don't prefix a struct field name here, we take care of that at asm generation time, which was a lot easier
|
||||
//nodesToPrefix += node.parent to node.parent.children.indexOf(node)
|
||||
return
|
||||
} else
|
||||
throw AssemblyError("can only use deref expression on struct type")
|
||||
} else {
|
||||
TODO("handle other left operand ${pexpr.left }in dereferencing of struct fields ${node.position}")
|
||||
}
|
||||
}
|
||||
// normal (non pointer deref) expression
|
||||
var lookupName = node.name
|
||||
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
|
||||
lookupName = lookupName.dropLast(4)
|
||||
}
|
||||
val stNode = st.lookup(lookupName) ?:
|
||||
throw AssemblyError("unknown identifier $node")
|
||||
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
@@ -98,28 +81,8 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
is PtStructDecl -> prefixNamedNode(node) // note: field names are not prefixed here, we take care of that at asm generation time, which was a lot easier
|
||||
is PtBuiltinFunctionCall -> {
|
||||
// could be a struct instance creation
|
||||
if(node.name=="prog8_lib_structalloc") {
|
||||
val struct = node.type.subType!!
|
||||
if(struct is StStruct) {
|
||||
// update link to active symboltable node
|
||||
node.type.subType = st.lookup(struct.scopedNameString) as StStruct
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> { }
|
||||
}
|
||||
|
||||
if(node is IPtVariable) {
|
||||
if(node.type.subType!=null)
|
||||
variablesToFixSubtype.add(node)
|
||||
} else if(node is PtExpression) {
|
||||
if(node.type.subType!=null)
|
||||
expressionsToFixSubtype.add(node)
|
||||
}
|
||||
|
||||
node.children.forEach { prefixSymbols(it) }
|
||||
}
|
||||
|
||||
@@ -176,40 +139,8 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val updatedSt = SymbolTableMaker(program, options).make()
|
||||
|
||||
fun findSubtypeReplacement(sub: ISubType): StStruct? {
|
||||
if(updatedSt.lookup(sub.scopedNameString)!=null)
|
||||
return null
|
||||
val old = st.lookup(sub.scopedNameString)
|
||||
if(old==null)
|
||||
throw AssemblyError("old subtype not found: ${sub.scopedNameString}")
|
||||
|
||||
val prefixed = ArrayDeque<String>()
|
||||
var node: StNode = old
|
||||
while(node.type!= StNodeType.GLOBAL) {
|
||||
val typeChar = typePrefixChar(node.type)
|
||||
prefixed.addFirst("p8${typeChar}_${node.name}")
|
||||
node=node.parent
|
||||
}
|
||||
return updatedSt.lookup(prefixed.joinToString(".")) as StStruct
|
||||
}
|
||||
|
||||
expressionsToFixSubtype.forEach { node ->
|
||||
findSubtypeReplacement(node.type.subType!!)?.let {
|
||||
node.type.subType = it
|
||||
}
|
||||
}
|
||||
variablesToFixSubtype.forEach { node ->
|
||||
findSubtypeReplacement(node.type.subType!!)?.let {
|
||||
node.type.subType = it
|
||||
}
|
||||
}
|
||||
|
||||
return updatedSt
|
||||
return SymbolTableMaker(program, options).make()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun prefixScopedName(name: String, type: Char): String {
|
||||
@@ -244,10 +175,10 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
newValue.add(elt)
|
||||
else {
|
||||
val newAddr = PtAddressOf(elt.type, false, elt.position)
|
||||
newAddr.add(elt.identifier!!.prefix(newAddr, st))
|
||||
if (elt.arrayIndexExpr != null)
|
||||
newAddr.add(elt.arrayIndexExpr!!)
|
||||
val newAddr = PtAddressOf(elt.position)
|
||||
newAddr.children.add(elt.identifier.prefix(newAddr, st))
|
||||
if(elt.arrayIndexExpr!=null)
|
||||
newAddr.children.add(elt.arrayIndexExpr!!)
|
||||
newAddr.parent = arrayValue
|
||||
newValue.add(newAddr)
|
||||
}
|
||||
@@ -276,34 +207,19 @@ private fun PtFunctionCall.withNewName(name: String): PtFunctionCall {
|
||||
}
|
||||
|
||||
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
val targetNt: StNodeType
|
||||
val target = st.lookup(name)
|
||||
var target = st.lookup(name)
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
return this
|
||||
|
||||
if(target==null) {
|
||||
if(name.endsWith("_lsb") || name.endsWith("_msb")) {
|
||||
val target2 = st.lookup(name.dropLast(4))
|
||||
if(target2?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
target = st.lookup(name.dropLast(4))
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
return this
|
||||
targetNt = target2!!.type
|
||||
} else {
|
||||
// if no target found, assume that the identifier is a struct field
|
||||
targetNt = StNodeType.STATICVAR
|
||||
}
|
||||
} else {
|
||||
targetNt = target.type
|
||||
}
|
||||
|
||||
val prefixType = typePrefixChar(targetNt)
|
||||
val newName = prefixScopedName(name, prefixType)
|
||||
val node = PtIdentifier(newName, type, position)
|
||||
node.parent = parent
|
||||
return node
|
||||
}
|
||||
|
||||
private fun typePrefixChar(targetNt: StNodeType): Char {
|
||||
return when(targetNt) {
|
||||
val prefixType = when(target!!.type) {
|
||||
StNodeType.BLOCK -> 'b'
|
||||
StNodeType.SUBROUTINE, StNodeType.EXTSUB -> 's'
|
||||
StNodeType.LABEL -> 'l'
|
||||
@@ -311,10 +227,12 @@ private fun typePrefixChar(targetNt: StNodeType): Char {
|
||||
StNodeType.CONSTANT -> 'c'
|
||||
StNodeType.BUILTINFUNC -> 's'
|
||||
StNodeType.MEMORYSLAB -> 'v'
|
||||
StNodeType.STRUCT -> 't'
|
||||
StNodeType.STRUCTINSTANCE -> 'i'
|
||||
else -> '?'
|
||||
}
|
||||
val newName = prefixScopedName(name, prefixType)
|
||||
val node = PtIdentifier(newName, type, position)
|
||||
node.parent = parent
|
||||
return node
|
||||
}
|
||||
|
||||
|
||||
@@ -337,17 +255,10 @@ class AsmGen6502Internal (
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val pointerGen = PointerAssignmentsGen(this, allocator)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, pointerGen, anyExprGen, allocator)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, pointerGen, assignmentAsmGen, errors)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors)
|
||||
private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors)
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, assignmentAsmGen, this, pointerGen, allocator)
|
||||
|
||||
init {
|
||||
assignmentAsmGen.augmentableAsmGen = augmentableAsmGen
|
||||
pointerGen.augmentableAsmGen = augmentableAsmGen
|
||||
}
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
@@ -405,9 +316,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allVariables.isNotEmpty()) {
|
||||
println("Static variables (not in ZeroPage):")
|
||||
symbolTable.allVariables
|
||||
.filterNot { allocator.isZpVar(it.scopedNameString) }
|
||||
.sortedBy { it.scopedNameString }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedNameString}\t")
|
||||
.filterNot { allocator.isZpVar(it.scopedName) }
|
||||
.sortedBy { it.scopedName }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedName}\t")
|
||||
}
|
||||
}
|
||||
if(allocator.globalFloatConsts.isNotEmpty()) {
|
||||
@@ -419,9 +330,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allMemMappedVariables.isNotEmpty()) {
|
||||
println("Memory mapped:")
|
||||
symbolTable.allMemMappedVariables
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedNameString} ))
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedName} ))
|
||||
.forEach { mvar ->
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedNameString}")
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedName}")
|
||||
}
|
||||
}
|
||||
if(symbolTable.allMemorySlabs.isNotEmpty()) {
|
||||
@@ -509,7 +420,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
|
||||
val name = asmVariableName(st.scopedNameString)
|
||||
val name = asmVariableName(st.scopedName)
|
||||
if(scope==null)
|
||||
return name
|
||||
// remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
|
||||
@@ -523,7 +434,7 @@ class AsmGen6502Internal (
|
||||
|
||||
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||
// returns the source name of the zero page pointervar if it's already in the ZP,
|
||||
// otherwise returns "P8ZP_SCRATCH_PTR" which is the intermediary
|
||||
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary
|
||||
val symbol = symbolTable.lookup(pointervar.name)
|
||||
when (val target = symbol!!.astNode) {
|
||||
is PtLabel -> {
|
||||
@@ -542,10 +453,10 @@ class AsmGen6502Internal (
|
||||
out("""
|
||||
lda $sourceName
|
||||
ldy $sourceName+1
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
lda (P8ZP_SCRATCH_PTR)""")
|
||||
"P8ZP_SCRATCH_PTR"
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda (P8ZP_SCRATCH_W1)""")
|
||||
"P8ZP_SCRATCH_W1"
|
||||
}
|
||||
} else {
|
||||
return if (allocator.isZpVar((target as PtNamedNode).scopedName)) {
|
||||
@@ -556,11 +467,11 @@ class AsmGen6502Internal (
|
||||
out("""
|
||||
lda $sourceName
|
||||
ldy $sourceName+1
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_PTR),y""")
|
||||
"P8ZP_SCRATCH_PTR"
|
||||
lda (P8ZP_SCRATCH_W1),y""")
|
||||
"P8ZP_SCRATCH_W1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -577,10 +488,10 @@ class AsmGen6502Internal (
|
||||
} else {
|
||||
out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
sta (P8ZP_SCRATCH_PTR)""")
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
sta (P8ZP_SCRATCH_W2)""")
|
||||
}
|
||||
} else {
|
||||
if (allocator.isZpVar(pointervar.name)) {
|
||||
@@ -589,11 +500,11 @@ class AsmGen6502Internal (
|
||||
} else {
|
||||
out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_PTR),y""")
|
||||
sta (P8ZP_SCRATCH_W2),y""")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -710,7 +621,7 @@ class AsmGen6502Internal (
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtNodeGroup -> stmt.children.forEach { translate(it) }
|
||||
is PtJmpTable -> translate(stmt)
|
||||
is PtNop, is PtStructDecl, is PtSubSignature -> {}
|
||||
is PtNop -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
}
|
||||
@@ -719,8 +630,6 @@ class AsmGen6502Internal (
|
||||
val reg = register.toString().lowercase()
|
||||
val indexnum = expr.index.asConstInteger()
|
||||
if (indexnum != null) {
|
||||
if(indexnum > 255)
|
||||
throw AssemblyError("array index $indexnum is larger than a byte ${expr.position}")
|
||||
val indexValue = if(expr.splitWords)
|
||||
indexnum
|
||||
else
|
||||
@@ -729,9 +638,6 @@ class AsmGen6502Internal (
|
||||
return
|
||||
}
|
||||
|
||||
if(!expr.index.type.isByte)
|
||||
throw AssemblyError("array index $indexnum is larger than a byte ${expr.position}")
|
||||
|
||||
if(expr.splitWords) {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||
return
|
||||
@@ -811,15 +717,15 @@ class AsmGen6502Internal (
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if (isTargetCpu(CpuType.CPU6502))
|
||||
out(" lda #0 | sta ${target.asmVarname}")
|
||||
out("lda #0 | sta ${target.asmVarname}")
|
||||
else
|
||||
out(" stz ${target.asmVarname}")
|
||||
out("stz ${target.asmVarname}")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
val address = target.memory!!.address.asConstInteger()
|
||||
if(address!=null) {
|
||||
if (isTargetCpu(CpuType.CPU6502))
|
||||
out(" lda #0 | sta ${address.toHex()}")
|
||||
out("lda #0 | sta ${address.toHex()}")
|
||||
else
|
||||
out(" stz ${address.toHex()}")
|
||||
return
|
||||
@@ -831,10 +737,6 @@ class AsmGen6502Internal (
|
||||
assignExpressionToRegister(zero, target.register!!)
|
||||
return
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("assign to pointer ${target.position}")
|
||||
return
|
||||
}
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
@@ -842,7 +744,6 @@ class AsmGen6502Internal (
|
||||
assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
|
||||
}
|
||||
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
|
||||
target.datatype.isWord || target.datatype.isPassByRef -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
translateNormalAssignment(
|
||||
@@ -860,7 +761,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun branchInstruction(condition: BranchCondition, complement: Boolean) =
|
||||
private fun branchInstruction(condition: BranchCondition, complement: Boolean) =
|
||||
if(complement) {
|
||||
when (condition) {
|
||||
BranchCondition.CS -> "bcc"
|
||||
@@ -895,7 +796,7 @@ class AsmGen6502Internal (
|
||||
when {
|
||||
iterations == 0 -> {}
|
||||
iterations == 1 -> translate(stmt.statements)
|
||||
iterations !in 0..65536 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations<0 || iterations>65536 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations <= 256 -> repeatByteCount(iterations, stmt)
|
||||
else -> repeatWordCount(iterations, stmt)
|
||||
}
|
||||
@@ -937,7 +838,7 @@ class AsmGen6502Internal (
|
||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt)
|
||||
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
|
||||
out("""
|
||||
ldy #>$loopcount
|
||||
@@ -957,7 +858,7 @@ $repeatLabel""")
|
||||
// note: A/Y must have been loaded with the number of iterations!
|
||||
// the iny + double dec is microoptimization of the 16 bit loop
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, false, stmt)
|
||||
out("""
|
||||
cmp #0
|
||||
beq +
|
||||
@@ -1145,18 +1046,17 @@ $repeatLabel""")
|
||||
if(evaluateAddressExpression) {
|
||||
val arrayIdx = jump.target as? PtArrayIndexer
|
||||
if (arrayIdx!=null) {
|
||||
val arrayVariable = arrayIdx.variable ?: TODO("support for ptr indexing ${arrayIdx.position}")
|
||||
if (isTargetCpu(CpuType.CPU65C02)) {
|
||||
if (!arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// on the 65c02, more optimal assembly can be generated using JMP (address,X)
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
|
||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||
} else {
|
||||
// print a message when more optimal code is possible for 65C02 cpu
|
||||
val variable = symbolTable.lookup(arrayVariable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128u)
|
||||
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128)
|
||||
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
|
||||
}
|
||||
} else {
|
||||
@@ -1175,20 +1075,18 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
private fun translate(ret: PtReturn) {
|
||||
val returnvalue = ret.children.singleOrNull() as? PtExpression
|
||||
val returnvalue = ret.children.singleOrNull()
|
||||
val sub = ret.definingSub()!!
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
|
||||
if(returnvalue!=null) {
|
||||
val returnDt = sub.signature.returns.single()
|
||||
if (returnDt.isNumericOrBool || returnDt.isPointer) {
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
|
||||
if (sub.returns.single().isNumericOrBool) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
val addrOfDt = returnvalue.type.typeForAddressOf(false)
|
||||
val addrofValue = PtAddressOf(addrOfDt, false, returnvalue.position)
|
||||
addrofValue.add(returnvalue)
|
||||
val addrofValue = PtAddressOf(returnvalue.position)
|
||||
addrofValue.add(returnvalue as PtIdentifier)
|
||||
addrofValue.parent = ret.parent
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
|
||||
}
|
||||
@@ -1315,11 +1213,11 @@ $repeatLabel""")
|
||||
return null
|
||||
val leftDt = left.type
|
||||
val rightDt = right.type
|
||||
if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedByte)
|
||||
if(leftDt.isUnsignedWord && rightDt.isUnsignedByte)
|
||||
return Pair(left, right)
|
||||
if(leftDt.isUnsignedByte && rightDt.isUnsignedWord)
|
||||
return Pair(right, left)
|
||||
if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedWord) {
|
||||
if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) {
|
||||
// could be that the index was a constant numeric byte but converted to word, check that
|
||||
val constIdx = right as? PtNumber
|
||||
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
|
||||
@@ -1355,7 +1253,9 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr) ?: return false
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
if(write) {
|
||||
|
||||
// WRITING TO pointer + offset
|
||||
@@ -1365,10 +1265,8 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
|
||||
} else {
|
||||
out(" sta ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
|
||||
out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1407,10 +1305,8 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
TODO("read &dereference")
|
||||
} else {
|
||||
out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
|
||||
out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1447,7 +1343,9 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
else if(addressExpr.operator=="-") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true) ?: return false
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
if(write) {
|
||||
|
||||
// WRITING TO pointer - offset
|
||||
@@ -1457,10 +1355,8 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
|
||||
} else {
|
||||
out(" sta ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
|
||||
out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1490,10 +1386,8 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
TODO("read &dereference")
|
||||
} else {
|
||||
out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
|
||||
out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1524,15 +1418,7 @@ $repeatLabel""")
|
||||
val node = stScope.astNode
|
||||
if(node is PtSubroutineParameter)
|
||||
return node
|
||||
val params = node!!.definingSub()?.signature?.children
|
||||
if(params!=null) {
|
||||
for(param in params) {
|
||||
param as PtSubroutineParameter
|
||||
if(param.scopedName==name)
|
||||
return param
|
||||
}
|
||||
}
|
||||
return null
|
||||
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
@@ -1698,10 +1584,6 @@ $repeatLabel""")
|
||||
ifExpressionAsmgen.assignIfExpression(target, value)
|
||||
}
|
||||
|
||||
internal fun assignBranchCondExpression(target: AsmAssignTarget, value: PtBranchCondExpression) {
|
||||
ifExpressionAsmgen.assignBranchCondExpression(target, value)
|
||||
}
|
||||
|
||||
internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) {
|
||||
val compare = if(useSbc) "sec | sbc" else "cmp"
|
||||
fun cmpViaScratch() {
|
||||
@@ -1722,9 +1604,7 @@ $repeatLabel""")
|
||||
if(constIndex!=null) {
|
||||
val offset = program.memsizer.memorySize(value.type, constIndex)
|
||||
if(offset<256) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable!!)},y")
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
|
||||
}
|
||||
}
|
||||
cmpViaScratch()
|
||||
@@ -1895,372 +1775,7 @@ $repeatLabel""")
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun loadIndirectByte(zpPtrVar: String, offset: UByte) {
|
||||
// loads byte pointed to by the ptrvar into A
|
||||
if (offset > 0u) {
|
||||
out(" ldy #$offset | lda ($zpPtrVar),y")
|
||||
} else {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out(" lda ($zpPtrVar)")
|
||||
else
|
||||
out(" ldy #0 | lda ($zpPtrVar),y")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadIndirectFloat(zpPtrVar: String, offset: UByte) {
|
||||
// loads float pointed to by the ptrvar into FAC1
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
clc
|
||||
adc #$offset
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.MOVFM""")
|
||||
return
|
||||
}
|
||||
|
||||
out("""
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.MOVFM""")
|
||||
}
|
||||
|
||||
internal fun loadIndirectWord(zpPtrVar: String, offset: UByte) {
|
||||
// loads word pointed to by the ptr var into AY
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
tax
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
tay
|
||||
txa""")
|
||||
} else {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out("""
|
||||
ldy #1
|
||||
lda ($zpPtrVar),y
|
||||
tay
|
||||
lda ($zpPtrVar)""")
|
||||
else
|
||||
out("""
|
||||
ldy #0
|
||||
lda ($zpPtrVar),y
|
||||
tax
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
|
||||
if (offset > 0u) {
|
||||
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
|
||||
} else {
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
out(" lda #$byte | sta ($zpPtrVar)")
|
||||
} else {
|
||||
if (byte == 0)
|
||||
out(" lda #0 | tay | sta ($zpPtrVar),y")
|
||||
else
|
||||
out(" lda #$byte | ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectByteVar(varname: String, zpPtrVar: String, offset: UByte) {
|
||||
if (offset > 0u) {
|
||||
out(" lda $varname | ldy #$offset | sta ($zpPtrVar),y")
|
||||
} else {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out(" lda $varname | sta ($zpPtrVar)")
|
||||
else
|
||||
out(" lda $varname | ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectWord(word: Int, zpPtrVar: String, offset: UByte) {
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
lda #<$word
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
lda #>$word
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
} else {
|
||||
if(word==0) {
|
||||
out("""
|
||||
lda #0
|
||||
tay
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
} else {
|
||||
out("""
|
||||
lda #<$word
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda #>$word
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectByteReg(
|
||||
register: CpuRegister,
|
||||
zpPtrVar: String,
|
||||
offset: UByte,
|
||||
signed: Boolean,
|
||||
extendWord: Boolean
|
||||
) {
|
||||
if(extendWord) {
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> out(" txa")
|
||||
CpuRegister.Y -> out(" tya")
|
||||
}
|
||||
signExtendAXlsb(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
|
||||
out("""
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
txa
|
||||
sta ($zpPtrVar),y""")
|
||||
return
|
||||
}
|
||||
|
||||
if(offset > 0u) {
|
||||
when(register) {
|
||||
CpuRegister.A -> out(" ldy #$offset | sta ($zpPtrVar),y")
|
||||
CpuRegister.X -> out(" txa | ldy #$offset | sta ($zpPtrVar),y")
|
||||
CpuRegister.Y -> out(" tya | ldy #$offset | sta ($zpPtrVar),y")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
when(register) {
|
||||
CpuRegister.A -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
CpuRegister.X -> {
|
||||
out(" txa")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
out(" tya")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectWordReg(regs: RegisterOrPair, zpPtrVar: String, offset: UByte) {
|
||||
if (offset > 0u) {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> {
|
||||
out("""
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
txa
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
RegisterOrPair.AY -> {
|
||||
out("""
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
RegisterOrPair.XY -> {
|
||||
out("""
|
||||
sty P8ZP_SCRATCH_REG
|
||||
txa
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
|
||||
out("""
|
||||
lda $regname
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
lda $regname+1
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
else -> throw AssemblyError("wrong word reg")
|
||||
}
|
||||
} else {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out("""
|
||||
sta ($zpPtrVar)
|
||||
txa
|
||||
ldy #1
|
||||
sta ($zpPtrVar),y""")
|
||||
else
|
||||
out("""
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
txa
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
RegisterOrPair.AY -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out("""
|
||||
sta ($zpPtrVar)
|
||||
tya
|
||||
ldy #1
|
||||
sta ($zpPtrVar),y""")
|
||||
else
|
||||
out("""
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
RegisterOrPair.XY -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out("""
|
||||
txa
|
||||
sta ($zpPtrVar)
|
||||
tya
|
||||
ldy #1
|
||||
sta ($zpPtrVar),y""")
|
||||
else
|
||||
out("""
|
||||
sty P8ZP_SCRATCH_REG
|
||||
txa
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
|
||||
out("""
|
||||
lda $regname
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda $regname+1
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
else -> throw AssemblyError("wrong word reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectWordVar(varname: String, sourceDt: DataType, zpPtrVar: String, offset: UByte) {
|
||||
if(sourceDt.isByteOrBool) TODO("implement byte/bool to word pointer assignment")
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
lda $varname
|
||||
ldy #$offset
|
||||
sta ($zpPtrVar),y
|
||||
lda $varname+1
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
} else {
|
||||
if(isTargetCpu(CpuType.CPU65C02))
|
||||
out("""
|
||||
lda $varname
|
||||
sta ($zpPtrVar)
|
||||
lda $varname+1
|
||||
ldy #1
|
||||
sta ($zpPtrVar),y""")
|
||||
else
|
||||
out("""
|
||||
lda $varname
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda $varname+1
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun storeIndirectFloat(float: Double, zpPtrVar: String, offset: UByte) {
|
||||
val floatConst = allocator.getFloatAsmConst(float)
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
clc
|
||||
adc #$offset
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.copy_float2""")
|
||||
return
|
||||
}
|
||||
|
||||
out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.copy_float2""")
|
||||
}
|
||||
|
||||
internal fun storeIndirectFloatVar(varname: String, zpPtrVar: String, offset: UByte) {
|
||||
if (offset > 0u) {
|
||||
out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
clc
|
||||
adc #$offset
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.copy_float""")
|
||||
return
|
||||
}
|
||||
|
||||
out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
|
||||
|
||||
internal fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
|
||||
fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
|
||||
if(options.romable) {
|
||||
// until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode...
|
||||
errors.warn("problem for ROMable code: $problem", pos)
|
||||
|
@@ -73,13 +73,6 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
@@ -415,7 +408,7 @@ private fun optimizeStoreLoadSame(
|
||||
// a branch instruction follows, we can only remove the load instruction if
|
||||
// another load instruction of the same register precedes the store instruction
|
||||
// (otherwise wrong cpu flags are used)
|
||||
val loadinstruction = second.take(3)
|
||||
val loadinstruction = second.substring(0, 3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
@@ -453,26 +446,15 @@ private fun optimizeStoreLoadSame(
|
||||
|
||||
|
||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ")
|
||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
|
||||
) {
|
||||
val firstVal = first.substring(4).trimStart()
|
||||
val thirdVal = third.substring(4).trimStart()
|
||||
if (firstVal == thirdVal)
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
@@ -712,32 +694,23 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
optimize('x', lines)
|
||||
optimize('y', lines)
|
||||
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
val third = lines[3].value.trimStart()
|
||||
|
||||
// phy + ldy + pla -> tya + ldy
|
||||
// phx + ldx + pla -> txa + ldx
|
||||
// pha + lda + pla -> nop
|
||||
// pha + tya + tay + pla -> nop
|
||||
// pha + txa + tax + pla -> nop
|
||||
when (first) {
|
||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " tya"))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " txa"))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
"pha" if ((second=="tya" && third=="tay") || (second=="txa" && third=="tax")) && fourth=="pla" -> {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
@@ -749,6 +722,7 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
return mods
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -793,120 +767,3 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
/*
|
||||
; FIRST SEQUYENCE: P8ZP_SCRATCH_PTR += AY :
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
pha
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
tay
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
|
||||
->
|
||||
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
sta P8ZP_SCRATCH_PTR+1
|
||||
|
||||
|
||||
also SECOND SEQUENCE:
|
||||
|
||||
ldx VALUE/ ldy VALUE
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
txa / tya
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
lda VALUE
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
|
||||
|
||||
also THIRD SEQUENCE:
|
||||
|
||||
ldx VALUE
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
txa
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
lda VALUE
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
*/
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFourteen) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
val fifth = lines[4].value.trimStart()
|
||||
val sixth = lines[5].value.trimStart()
|
||||
val seventh = lines[6].value.trimStart()
|
||||
val eight = lines[7].value.trimStart()
|
||||
val ninth = lines[8].value.trimStart()
|
||||
|
||||
// FIRST SEQUENCE
|
||||
if(first=="clc" && second.startsWith("adc") && third=="pha" && fourth=="tya" &&
|
||||
fifth.startsWith("adc") && sixth=="tay" && seventh=="pla" && eight.startsWith("sta") && ninth.startsWith("sty")) {
|
||||
val var2 = second.substring(4)
|
||||
val var5 = fifth.substring(4).substringBefore('+')
|
||||
val var8 = eight.substring(4)
|
||||
val var9 = ninth.substring(4).substringBefore('+')
|
||||
if(var2==var5 && var2==var8 && var2==var9) {
|
||||
if(fifth.endsWith("$var5+1") && ninth.endsWith("$var9+1")) {
|
||||
mods.add(Modification(lines[2].index, false, " sta $var2"))
|
||||
mods.add(Modification(lines[5].index, false, " sta $var2+1"))
|
||||
mods.add(Modification(lines[6].index, true, null))
|
||||
mods.add(Modification(lines[7].index, true, null))
|
||||
mods.add(Modification(lines[8].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SECOND SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("sta ") &&
|
||||
third=="txa" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
if(first.startsWith("ldy ") && second.startsWith("sta ") &&
|
||||
third=="tya" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",y" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
// THIRD SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("ldy ") && third.startsWith("sta ") &&
|
||||
fourth=="txa" && fifth=="iny" && sixth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in third) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[3].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return mods
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -47,7 +46,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peekbool" -> funcPeekBool(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
@@ -62,14 +60,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"pokebool" -> funcPokeBool(fcall)
|
||||
"rsave" -> funcRsave()
|
||||
"rrestore" -> funcRrestore()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
||||
@@ -384,9 +380,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
@@ -395,21 +390,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val slabname = SymbolTable.labelnameForStructInstance(fcall)
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(PtIdentifier(slabname, fcall.type, fcall.position))
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type.base) {
|
||||
@@ -435,11 +415,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -461,10 +438,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
@@ -490,10 +465,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -526,9 +498,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
@@ -551,10 +521,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -577,9 +545,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||
else
|
||||
@@ -605,9 +571,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -640,9 +604,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
@@ -671,7 +633,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
|
||||
val arrayVariable = indexer.variable
|
||||
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
|
||||
indexer.children[0].parent = indexer
|
||||
elementSize = 1
|
||||
@@ -792,52 +754,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPokeBool(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" sta $addr")
|
||||
return
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, varname, 0u, false, false)
|
||||
return
|
||||
}
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" sta ($varname),y")
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> { /* fall through */ }
|
||||
}
|
||||
|
||||
// fall through method:
|
||||
if(fcall.args[1].isSimple()) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
} else {
|
||||
asmgen.pushCpuStack(BaseDataType.UBYTE, fcall.args[1])
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W1", 0u, false, false)
|
||||
}
|
||||
|
||||
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
@@ -851,7 +767,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
asmgen.storeIndirectWordReg(RegisterOrPair.AX, varname, 0u)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
sta ($varname)
|
||||
txa
|
||||
ldy #1
|
||||
sta ($varname),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -886,46 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), register=resultRegister, position=fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeekBool(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peek")
|
||||
}
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" lda $addr")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectByte(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.out(" lda ($varname),y")
|
||||
} else fallback()
|
||||
}
|
||||
else -> fallback()
|
||||
}
|
||||
|
||||
when(resultRegister ?: RegisterOrPair.A) {
|
||||
RegisterOrPair.A -> {}
|
||||
RegisterOrPair.X -> asmgen.out(" tax")
|
||||
RegisterOrPair.Y -> asmgen.out(" tay")
|
||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().lowercase()}")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,10 +831,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectWord(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($varname),y
|
||||
tay
|
||||
lda ($varname)""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($varname),y
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
} else fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
@@ -1207,9 +1112,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the msb byte out of the word array
|
||||
if(arg.splitWords) {
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1226,9 +1129,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1310,10 +1211,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the lsb byte out of the word array
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1378,7 +1276,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||
return when (value) {
|
||||
is PtIdentifier -> {
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1392,7 +1290,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
@@ -1412,7 +1310,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1430,7 +1328,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
|
@@ -500,10 +500,10 @@ $endLabel""")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0u
|
||||
else -> 0
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
@@ -549,7 +549,7 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -565,7 +565,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@@ -592,7 +592,7 @@ $loopLabel sty $indexVar
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -608,7 +608,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@@ -635,12 +635,12 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=127u) {
|
||||
if(numElements<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #${numElements*2u}
|
||||
cpy #${numElements*2}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
@@ -653,7 +653,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||
// Just load them in whatever the register spec says.
|
||||
return when (params.size) {
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
1 -> params[0].type.isIntegerOrBool && params[0].register == null
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||
else -> false
|
||||
}
|
||||
@@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||
if(symbol.type == StNodeType.LABEL) {
|
||||
require(call.void)
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -139,16 +139,15 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(optimizeIntArgsViaCpuRegisters(parameters)) {
|
||||
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||
// Note that if the args fit into cpu registers, we don't concern ourselves here
|
||||
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
|
||||
// That is now up to the subroutine itself.
|
||||
useCpuRegistersForArgs(call.args, sub)
|
||||
} else {
|
||||
// arguments via variables
|
||||
val paramValues = parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
|
||||
val paramValues = sub.parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||
if (normalParams.isNotEmpty()) {
|
||||
for (p in normalParams)
|
||||
argumentViaVariable(sub, p.first, p.second)
|
||||
@@ -167,7 +166,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
|
||||
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
val params = sub.parameters
|
||||
when(params.size) {
|
||||
1 -> {
|
||||
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||
@@ -333,14 +332,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
} else {
|
||||
val scope = value.definingISub()
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type.isByte && register.isWord())
|
||||
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||
else {
|
||||
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
|
||||
}
|
||||
val src = if(value.type.isPassByRef) {
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
|
@@ -6,13 +6,11 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||
import prog8.codegen.cpu6502.assignment.PointerAssignmentsGen
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
|
||||
internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private val st: SymbolTable,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val pointergen: PointerAssignmentsGen,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val errors: IErrorReporter) {
|
||||
|
||||
@@ -45,7 +43,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val rightDt = compareCond.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -65,16 +63,6 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val dereference = stmt.condition as? PtPointerDeref
|
||||
if(dereference!=null) {
|
||||
val (zpPtrVar, offset) = pointergen.deref(dereference)
|
||||
asmgen.loadIndirectByte(zpPtrVar, offset)
|
||||
return if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
|
||||
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
||||
}
|
||||
|
||||
@@ -897,9 +885,7 @@ _jump jmp (${target.asmLabel})
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
@@ -915,8 +901,8 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
} else {
|
||||
var varname = asmgen.asmVariableName(value.identifier!!)
|
||||
if(value.identifier!!.type.isSplitWordArray) {
|
||||
var varname = asmgen.asmVariableName(value.identifier)
|
||||
if(value.identifier.type.isSplitWordArray) {
|
||||
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
|
||||
}
|
||||
asmgen.out(" lda #>$varname")
|
||||
@@ -994,10 +980,8 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -1143,10 +1127,8 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -1259,9 +1241,7 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
|
||||
}
|
||||
@@ -1282,9 +1262,7 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if (constIndex != null) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
|
||||
}
|
||||
@@ -1665,10 +1643,8 @@ _jump jmp (${target.asmLabel})
|
||||
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
|
||||
val constIndex = left.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if(left.variable==null)
|
||||
TODO("support for ptr indexing ${left.position}")
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varName = asmgen.asmVariableName(left.variable!!)
|
||||
val varName = asmgen.asmVariableName(left.variable)
|
||||
if(left.splitWords) {
|
||||
return if(notEquals)
|
||||
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
|
||||
@@ -1731,13 +1707,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement) {
|
||||
if(left.isFromArrayElement)
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
left.identifier!!.name
|
||||
left.identifier.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
@@ -1783,13 +1759,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement) {
|
||||
if(left.isFromArrayElement)
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
left.identifier!!.name
|
||||
left.identifier.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
|
@@ -9,9 +9,7 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
||||
|
||||
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
|
||||
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
||||
val endLabel = asmgen.makeLabel("ifexpr_end")
|
||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||
@@ -44,69 +42,6 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignBranchCondExpression(target: AsmAssignTarget, expr: PtBranchCondExpression) {
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
|
||||
if(target.kind==TargetStorageKind.REGISTER && target.datatype.isUnsignedByte && target.register==RegisterOrPair.A) {
|
||||
if(expr.condition==BranchCondition.CC) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
}
|
||||
else if(expr.condition==BranchCondition.CS) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val trueLabel = asmgen.makeLabel("branchexpr_true")
|
||||
val endLabel = asmgen.makeLabel("branchexpr_end")
|
||||
val branch = asmgen.branchInstruction(expr.condition, false)
|
||||
|
||||
asmgen.out(" $branch $trueLabel")
|
||||
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
when (condition) {
|
||||
is PtBinaryExpression -> {
|
||||
|
@@ -44,7 +44,6 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
|
||||
structInstances2asm()
|
||||
memorySlabs()
|
||||
footer()
|
||||
}
|
||||
@@ -71,7 +70,6 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_PTR = ${zp.SCRATCH_PTR} ; word")
|
||||
if(compTarget.name=="c64") {
|
||||
if(options.floats)
|
||||
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
|
||||
@@ -209,20 +207,18 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
for (slab in symboltable.allMemorySlabs) {
|
||||
if (slab.align > 1u)
|
||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
||||
}
|
||||
asmgen.out("\t.bend\n .send BSS_SLABS")
|
||||
asmgen.out("\t.bend\n .send slabs_BSS")
|
||||
}
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
asmgen.out(" .dsection STRUCTINSTANCES\n")
|
||||
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
@@ -278,14 +274,14 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
@@ -294,12 +290,12 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +348,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -366,93 +362,11 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
private fun asmTypeString(dt: DataType): String {
|
||||
return when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte"
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun structInstances2asm() {
|
||||
|
||||
fun initValues(instance: StStructInstance): List<String> {
|
||||
val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
|
||||
return structtype.fields.zip(instance.initialValues).map { (field, value) ->
|
||||
if(field.first.isFloat) {
|
||||
"["+compTarget.getFloatAsmBytes(value.number!!)+"]"
|
||||
} else {
|
||||
when {
|
||||
value.number!=null -> {
|
||||
if(field.first.isPointer)
|
||||
"$"+value.number!!.toInt().toString(16)
|
||||
else if(field.first.isInteger)
|
||||
value.number!!.toInt().toString()
|
||||
else
|
||||
value.number.toString()
|
||||
}
|
||||
value.addressOfSymbol!=null -> value.addressOfSymbol!!
|
||||
value.boolean!=null -> if(value.boolean==true) "1" else "0"
|
||||
else -> throw AssemblyError("weird struct initial value $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asmgen.out("; struct types")
|
||||
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
|
||||
asmgen.out("${it.structName} .struct $structargs\n")
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
val type = when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
|
||||
}
|
||||
asmgen.out(" .endstruct\n")
|
||||
}
|
||||
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
|
||||
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||
asmgen.out(" .section BSS\n")
|
||||
instancesNoInit.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val zerovalues = structtype.fields.map { field ->
|
||||
if(field.first.isFloat) {
|
||||
val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
|
||||
"[${floatbytes.joinToString(",")}]"
|
||||
}
|
||||
else "?"
|
||||
}
|
||||
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .send BSS\n")
|
||||
|
||||
asmgen.out("; struct instances with initialization values\n")
|
||||
asmgen.out(" .section STRUCTINSTANCES\n")
|
||||
instances.forEach { asmgen.out("${it.name} .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n") }
|
||||
asmgen.out(" .send STRUCTINSTANCES\n")
|
||||
}
|
||||
|
||||
internal fun translateAsmSubroutine(sub: PtAsmSub) {
|
||||
if(sub.inline) {
|
||||
return // subroutine gets inlined at call site.
|
||||
@@ -502,7 +416,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -520,7 +434,7 @@ internal class ProgramAndVarsGen(
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
val params = sub.parameters
|
||||
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||
|
||||
@@ -576,7 +490,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
@@ -631,12 +545,12 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, 0u, it.value.second, it.value.first)
|
||||
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("+")
|
||||
@@ -704,7 +618,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||
asmgen.out(" .section $section")
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0u }
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0 }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
@@ -729,8 +643,8 @@ internal class ProgramAndVarsGen(
|
||||
if(varsWithInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables with init value")
|
||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||
notAlignedStrings.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
@@ -777,27 +691,23 @@ internal class ProgramAndVarsGen(
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||
}
|
||||
dt.isArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun alignVar(align: UInt) {
|
||||
if(align > 1u)
|
||||
private fun alignVar(align: Int) {
|
||||
if(align > 1)
|
||||
asmgen.out(" .align ${align.toHex()}")
|
||||
}
|
||||
|
||||
@@ -834,7 +744,7 @@ internal class ProgramAndVarsGen(
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
dt.isArray -> {
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -842,7 +752,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||
alignVar(align)
|
||||
when {
|
||||
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||
@@ -944,7 +854,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
|
||||
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||
alignVar(align)
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
@@ -1009,7 +919,7 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
|
||||
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
@@ -60,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedNameString,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length?.toInt(),
|
||||
variable.length,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@@ -79,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedNameString,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length?.toInt(),
|
||||
variable.length,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@@ -92,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
// try to allocate the "don't care" interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedNameString,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length?.toInt(),
|
||||
variable.length,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
|
@@ -20,8 +20,6 @@ internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
if(expr.operator==".")
|
||||
throw AssemblyError("pointer deref expression should have been handled elsewhere ${expr.position}")
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
|
||||
@@ -48,12 +46,6 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
expr.type.isPointer -> {
|
||||
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
|
||||
"both operands must be pointers or uwords"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,11 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE, // non-pointer variable
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -33,7 +32,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val pointer: PtPointerDeref? = null,
|
||||
val origAstTarget: PtAssignTarget? = null
|
||||
)
|
||||
{
|
||||
@@ -41,24 +39,13 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
}
|
||||
|
||||
init {
|
||||
if(register!=null && !datatype.isNumericOrBool)
|
||||
throw AssemblyError("must be numeric type")
|
||||
if(kind==TargetStorageKind.REGISTER)
|
||||
require(register!=null)
|
||||
else
|
||||
require(register==null)
|
||||
if(kind==TargetStorageKind.POINTER)
|
||||
require(pointer!=null)
|
||||
if(pointer!=null)
|
||||
require(kind==TargetStorageKind.POINTER)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -90,7 +77,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||
pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@@ -148,14 +134,11 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
left is PtIdentifier && left.name==scopedName
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("is pointer deref target same as expression? ${this.position}")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> false
|
||||
TargetStorageKind.VOID -> false
|
||||
}
|
||||
@@ -177,11 +160,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
@@ -223,7 +203,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.signature.returns.size>1)
|
||||
if(sub is PtSub && sub.returns.size>1)
|
||||
DataType.UNDEFINED // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ import prog8.codegen.cpu6502.VariableAllocator
|
||||
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val ptrgen: PointerAssignmentsGen,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
|
||||
@@ -85,23 +84,22 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isWord || target.datatype.isPointer -> {
|
||||
target.datatype.isWord -> {
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
val targetDt = if(target.datatype.isWord) target.datatype else DataType.UWORD // pointers themselves that get a new value are just treated as UWORD variables
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, regName(value), value.datatype, block)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, targetDt, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.array!!, block)
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, target.datatype, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!, block)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block)
|
||||
}
|
||||
else {
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression!!, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,13 +224,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
TODO("inplace modification array indexed pointer deref ${target.position}")
|
||||
return
|
||||
}
|
||||
val targetArrayVar = target.array.variable!!
|
||||
val indexNum = target.array.index as? PtNumber
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
if (indexNum!=null) {
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
@@ -309,7 +301,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isPointer -> TODO("inplace modification of pointer array ${target.position}")
|
||||
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
}
|
||||
}
|
||||
@@ -328,7 +320,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
|
||||
@@ -374,7 +366,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${targetArrayVar.name},y")
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
}
|
||||
|
||||
target.datatype.isWord -> {
|
||||
@@ -385,11 +377,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
|
||||
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}_msb,y")
|
||||
} else {
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}+1,y")
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}+1,y")
|
||||
}
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
@@ -458,9 +450,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
|
||||
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y")
|
||||
else
|
||||
asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
|
||||
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
@@ -512,24 +504,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
pla ; restore array ptr lsb
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
|
||||
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
|
||||
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
TODO("indexed inc/dec on pointer ${array.position}")
|
||||
return false
|
||||
}
|
||||
val arrayvar = asmgen.asmVariableName(arrayVar)
|
||||
val arrayvar = asmgen.asmVariableName(array.variable)
|
||||
when {
|
||||
array.type.isByte -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
|
||||
@@ -585,7 +571,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(if(operator=="+") " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
return true
|
||||
}
|
||||
array.type.isPointer -> TODO("indexed inc/dec on pointer ${array.position}")
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
@@ -899,7 +884,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
|
||||
if (target.datatype == value.type || (target.datatype.isPointer && value.type.isWord)) {
|
||||
if (target.datatype == value.type) {
|
||||
val childDt = value.value.type
|
||||
if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) {
|
||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||
@@ -987,7 +972,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tinc $ffff\t; modified")
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
@@ -1007,7 +992,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tdec $ffff\t; modified")
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
@@ -1140,18 +1125,11 @@ $shortcutLabel:""")
|
||||
}
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
|
||||
// use the already existing optimized codegen for regular assignments x += array[index]
|
||||
val binexpr = PtBinaryExpression(operator, dt, value.position)
|
||||
binexpr.add(PtIdentifier(name, dt, value.position))
|
||||
val arrayValue = PtArrayIndexer(value.type, value.position)
|
||||
arrayValue.add(valueVar)
|
||||
arrayValue.add(value.variable)
|
||||
arrayValue.add(value.index)
|
||||
binexpr.add(arrayValue)
|
||||
binexpr.parent = value
|
||||
@@ -2278,7 +2256,7 @@ $shortcutLabel:""")
|
||||
|
||||
private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
|
||||
require(dt.isWord)
|
||||
require(valueDt.isInteger || valueDt.isPointer)
|
||||
require(valueDt.isInteger)
|
||||
when {
|
||||
valueDt.isByte -> {
|
||||
// the other variable is a BYTE type so optimize for that
|
||||
@@ -2488,7 +2466,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
valueDt.isWord -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
@@ -2910,7 +2888,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
valueDt.isWord -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
@@ -2925,12 +2903,7 @@ $shortcutLabel:""")
|
||||
"-" -> {
|
||||
if(value.index.type.isByte) {
|
||||
// it's an array indexed by a byte so we can use sbc array,y
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
val arrayname = valueVar.name
|
||||
val arrayname = value.variable.name
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords) {
|
||||
asmgen.out("""
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,7 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -9,7 +11,7 @@ dependencies {
|
||||
implementation(project(":codeGenIntermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -9,10 +11,12 @@ dependencies {
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -13,8 +13,9 @@
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -124,100 +124,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
val target = augAssign.target
|
||||
val targetDt = irType(target.type)
|
||||
val value = augAssign.value
|
||||
val memTarget = target.memory
|
||||
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
|
||||
val symbol = target.identifier?.name
|
||||
val array = target.array
|
||||
val value = augAssign.value
|
||||
val signed = target.type.isSigned
|
||||
val pointerDeref = target.pointerDeref
|
||||
val chunks: IRCodeChunks
|
||||
|
||||
if(pointerDeref!=null) {
|
||||
val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
|
||||
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
|
||||
val oldvalueReg = codeGen.registers.next(targetDt)
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||
operandTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
}
|
||||
} else {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"and=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
|
||||
chunks = inplaceInstrs
|
||||
} else {
|
||||
chunks = when (augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
} ?: fallbackAssign(augAssign)
|
||||
}
|
||||
val chunks = when (augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
} ?: fallbackAssign(augAssign)
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
|
||||
return chunks
|
||||
}
|
||||
@@ -310,47 +240,45 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val skipCarryLabel = codeGen.createLabelName()
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val registerLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||
val registerMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
|
||||
}
|
||||
result += IRCodeChunk(skipCarryLabel, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,47 +292,44 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"not" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val register = codeGen.registers.next(vmDt)
|
||||
if(constIndex!=null) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,7 +343,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val targetIdent = assignment.target.identifier
|
||||
val targetMemory = assignment.target.memory
|
||||
val targetArray = assignment.target.array
|
||||
val targetPointerDeref = assignment.target.pointerDeref
|
||||
val valueDt = irType(assignment.value.type)
|
||||
val targetDt = irType(assignment.target.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -476,14 +400,80 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
else if(targetArray!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(targetArray.type, null)
|
||||
val variable = targetArray.variable
|
||||
if(variable==null)
|
||||
translateRegularAssignPointerIndexed(result, targetArray.pointerderef!!, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else if(variable.type.isPointer)
|
||||
assignToIndexedSimplePointer(result, variable, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
val variable = targetArray.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type, null)
|
||||
|
||||
val fixedIndex = targetArray.index.asConstInteger()
|
||||
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(fixedIndex!=null) {
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
else if(targetMemory!=null) {
|
||||
@@ -507,33 +497,31 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -543,188 +531,29 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
return result
|
||||
}
|
||||
else if(targetPointerDeref!=null) {
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
|
||||
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
|
||||
return result
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
|
||||
private fun assignToIndexedSimplePointer(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
targetIdent: PtIdentifier,
|
||||
eltSize: Int,
|
||||
targetArray: PtArrayIndexer,
|
||||
zeroValue: Boolean,
|
||||
targetDt: IRDataType,
|
||||
valueRegister: Int,
|
||||
valueFpRegister: Int
|
||||
) {
|
||||
val pointerTr = expressionEval.translateExpression(targetIdent)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
val constIndex = targetArray.index.asConstInteger()
|
||||
if(zeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offset = eltSize * constIndex
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
|
||||
} else {
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
|
||||
}
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), true, -1)
|
||||
} else {
|
||||
if(constIndex!=null) {
|
||||
val offset = eltSize * constIndex
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
|
||||
} else {
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
|
||||
}
|
||||
val realValueReg = if(targetDt == IRDataType.FLOAT) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), false, realValueReg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateRegularAssignArrayIndexed(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
variable: String,
|
||||
eltSize: Int,
|
||||
targetArray: PtArrayIndexer,
|
||||
zero: Boolean,
|
||||
targetDt: IRDataType,
|
||||
valueRegister: Int,
|
||||
valueFpRegister: Int
|
||||
) {
|
||||
val fixedIndex = targetArray.index.asConstInteger()
|
||||
val arrayLength = codeGen.symbolTable.getLength(variable)
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_lsb")
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*eltSize
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(fixedIndex!=null) {
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateRegularAssignPointerIndexed(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
pointerderef: PtPointerDeref,
|
||||
eltSize: Int,
|
||||
targetArray: PtArrayIndexer,
|
||||
zero: Boolean,
|
||||
targetDt: IRDataType,
|
||||
valueRegister: Int,
|
||||
valueFpRegister: Int
|
||||
) {
|
||||
val pointerTr = expressionEval.translateExpression(pointerderef)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
|
||||
val fixedIndex = targetArray.index.asConstInteger()
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*eltSize
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
|
||||
if(zero) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
|
||||
} else {
|
||||
addInstr(
|
||||
result,
|
||||
if (targetDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueFpRegister, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg), null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// index is an expression
|
||||
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
|
||||
if(zero) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
|
||||
} else {
|
||||
addInstr(result,
|
||||
if(targetDt== IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 =valueFpRegister, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg)
|
||||
, null)
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(itemsize==1 || array.splitWords) {
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
return Pair(result, tr.resultReg)
|
||||
}
|
||||
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||
mult.children += array.index
|
||||
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
||||
val tr = expressionEval.translateExpression(mult)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
return Pair(result, tr.resultReg)
|
||||
}
|
||||
|
||||
private fun operatorAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
@@ -736,14 +565,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -765,7 +594,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun operatorLogicalAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
@@ -777,7 +605,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val valueReg = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -824,21 +652,20 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -868,11 +695,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(array.splitWords) {
|
||||
throw AssemblyError("logical or on (split) word array should not happen")
|
||||
} else {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val valueReg = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -978,12 +804,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(constValue!=1) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
val valueReg=codeGen.registers.next(eltDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1036,15 +861,13 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
if(constValue==1) {
|
||||
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val valueReg=codeGen.registers.next(eltDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1104,16 +927,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(constIndex!=null) {
|
||||
val skip = codeGen.createLabelName()
|
||||
if(constValue==1) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
val lsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null).also {
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
@@ -1123,8 +944,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return null // fallback to slow method // TODO("inplace split word array -")
|
||||
}
|
||||
|
||||
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?,
|
||||
vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(array.splitWords)
|
||||
@@ -1134,15 +954,13 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
if(constValue==1) {
|
||||
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val valueReg=codeGen.registers.next(elementDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1153,36 +971,39 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return null // TODO("optimized memory in-place +"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if (constAddress != null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if (constAddress != null)
|
||||
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress), null)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1195,12 +1016,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(constIndex!=null) {
|
||||
val skip = codeGen.createLabelName()
|
||||
if(constValue==1) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null)
|
||||
return result
|
||||
@@ -1218,26 +1037,24 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constValue = operand.asConstInteger()
|
||||
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
if(array.splitWords) {
|
||||
repeat(constValue) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
if(constValue==1) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1257,18 +1074,13 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
} else {
|
||||
val shiftTr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, shiftTr, shiftTr.resultReg, -1)
|
||||
var shiftReg = shiftTr.resultReg
|
||||
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
|
||||
shiftReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
|
||||
}
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||
val ins = if(constAddress!=null)
|
||||
IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, reg1 = shiftReg, labelSymbol = symbol)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
}
|
||||
return result
|
||||
@@ -1281,26 +1093,24 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val constValue = operand.asConstInteger()
|
||||
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
if(array.splitWords) {
|
||||
repeat(constValue) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
if(constValue==1) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1319,17 +1129,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
} else {
|
||||
val shiftTr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, shiftTr, shiftTr.resultReg, -1)
|
||||
var shiftReg = shiftTr.resultReg
|
||||
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
|
||||
shiftReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
|
||||
}
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
}
|
||||
return result
|
||||
@@ -1337,8 +1142,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun operatorXorInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
@@ -1350,14 +1153,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.next(vmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
@@ -28,12 +27,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
"peek" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekbool" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebool" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
@@ -50,8 +46,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -500,16 +494,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val labelname = SymbolTable.labelnameForStructInstance(call)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = labelname)
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -559,9 +545,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
if(arr.variable==null)
|
||||
TODO("support for ptr indexing ${arr.position}")
|
||||
val variable = arr.variable!!.name
|
||||
val variable = arr.variable.name
|
||||
if(arr.splitWords) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
when(opcodeMemAndReg.first) {
|
||||
@@ -630,9 +614,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(target.variable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -666,8 +648,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
else {
|
||||
val targetVariable = target.variable ?: TODO("support for ptr indexing ${target.position}")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(isConstZeroValue) {
|
||||
@@ -676,7 +656,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
@@ -686,7 +666,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -697,7 +677,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
@@ -707,7 +687,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -83,111 +83,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
is PtArrayIndexer -> translate(expr)
|
||||
is PtBinaryExpression -> translate(expr)
|
||||
is PtIfExpression -> translate(expr)
|
||||
is PtBranchCondExpression -> translate(expr)
|
||||
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
|
||||
is PtFunctionCall -> translate(expr)
|
||||
is PtContainmentCheck -> translate(expr)
|
||||
is PtPointerDeref -> translate(expr)
|
||||
is PtRange,
|
||||
is PtArray,
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
var pointerReg: Int
|
||||
|
||||
if(deref.startpointer.type.isStructInstance) {
|
||||
TODO("translate structinstance deref???")
|
||||
/*
|
||||
val arrayIndexer = deref.startpointer as? PtArrayIndexer
|
||||
if(arrayIndexer==null)
|
||||
throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
|
||||
// first evaluate the adress of POINTER[x].a
|
||||
// which is: value in pointer + x*sizeof(struct) + offsetof(a)
|
||||
// then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
|
||||
val struct = deref.startpointer.type.subType as StStruct
|
||||
val chain = ArrayDeque(deref.chain)
|
||||
val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
|
||||
val pointerTr = translateExpression(arrayIndexer.variable)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
val constIndex = arrayIndexer.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val offset = constIndex * struct.size.toInt()
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
|
||||
} else {
|
||||
val indexTr = translateExpression(arrayIndexer.index)
|
||||
result += indexTr.chunks
|
||||
// multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
|
||||
// and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val indexReg: Int
|
||||
if(arrayIndexer.index.type.isByte) {
|
||||
// extend array index to word
|
||||
indexReg = codeGen.registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
|
||||
} else {
|
||||
indexReg = indexTr.resultReg
|
||||
}
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
|
||||
}
|
||||
}
|
||||
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
|
||||
if (firstField.first.isPointer) {
|
||||
// get the address stored in the pointer and use that for the rest of the chain
|
||||
// LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
} else {
|
||||
require(chain.isEmpty())
|
||||
// it's a pointer to a simple value, so keep the pointer as-is
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field or on b.c.d.field if field isn't a field.
|
||||
val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
|
||||
actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
|
||||
actualDeref.add(arrayIndexer.variable)
|
||||
*/
|
||||
} else {
|
||||
val tr = translateExpression(deref.startpointer)
|
||||
result += tr.chunks
|
||||
pointerReg = tr.resultReg
|
||||
}
|
||||
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
|
||||
result += instructions
|
||||
if(offset<=0u) {
|
||||
val irdt = irType(deref.type)
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
// load field with offset
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val irdt = irType(deref.type)
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
|
||||
|
||||
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
||||
@@ -254,83 +158,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(branchExpr: PtBranchCondExpression): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val trueTr = translateExpression(branchExpr.truevalue)
|
||||
val falseTr = translateExpression(branchExpr.falsevalue)
|
||||
val trueLabel = codeGen.createLabelName()
|
||||
val endLabel = codeGen.createLabelName()
|
||||
val irDt = irType(branchExpr.type)
|
||||
|
||||
if(branchExpr.condition==BranchCondition.CC && irDt==IRDataType.BYTE) {
|
||||
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
else if(branchExpr.condition==BranchCondition.CS) {
|
||||
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
val branchInstr = codeGen.IRBranchInstr(branchExpr.condition, trueLabel)
|
||||
addInstr(result, branchInstr, null)
|
||||
|
||||
if (irDt != IRDataType.FLOAT) {
|
||||
addToResult(result, falseTr, trueTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
result += IRCodeChunk(trueLabel, null)
|
||||
addToResult(result, trueTr, trueTr.resultReg, -1)
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
} else {
|
||||
addToResult(result, falseTr, -1, trueTr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
result += IRCodeChunk(trueLabel, null)
|
||||
addToResult(result, trueTr, -1, trueTr.resultFpReg)
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return ExpressionCodeResult(result, irDt, -1, trueTr.resultFpReg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(expr: PtAddressOf): ExpressionCodeResult {
|
||||
val vmDt = irType(expr.type)
|
||||
val symbol = expr.identifier.name
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val identifier = expr.identifier
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
|
||||
fun loadAddressOfArrayLabel(reg: Int) {
|
||||
if (expr.isMsbForSplitArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
|
||||
} else if (identifier!!.type.isSplitWordArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
|
||||
} else if (expr.identifier.type.isSplitWordArray) {
|
||||
// the _lsb split array comes first in memory
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||
} else
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||
}
|
||||
|
||||
if(expr.isFromArrayElement) {
|
||||
@@ -341,55 +183,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
|
||||
ixWord
|
||||
} else indexTr.resultReg
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
if(identifier!!.type.isUnsignedWord) {
|
||||
if(expr.identifier.type.isUnsignedWord) {
|
||||
require(!expr.isMsbForSplitArray)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val ptr = codeGen.symbolTable.lookup(identifier.name)
|
||||
val ptr = codeGen.symbolTable.lookup(expr.identifier.name)
|
||||
it += if(ptr is StConstant)
|
||||
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
} else if(identifier.type.isPointer) {
|
||||
// apply pointer arithmetic for the array indexing
|
||||
val eltSize = if(identifier.type.sub!=null)
|
||||
codeGen.program.memsizer.memorySize(identifier.type.sub!!)
|
||||
else
|
||||
identifier.type.subType!!.memsize(codeGen.program.memsizer)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name), null)
|
||||
if (eltSize > 1) {
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
}
|
||||
} else {
|
||||
// regular array indexing
|
||||
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
|
||||
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else if(expr.identifier!=null ) {
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
val pointerTr = translateExpression(expr.dereference!!.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
|
||||
result += instructions
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
|
||||
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
}
|
||||
|
||||
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
||||
@@ -403,32 +220,31 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
val ptrWithOffset = mem.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
|
||||
val tr = translateExpression(mem.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -527,30 +343,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
|
||||
if(arrayIx.type.isStructInstance)
|
||||
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
|
||||
val vmDt = irType(arrayIx.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVar = arrayIx.variable
|
||||
if(arrayVar==null) {
|
||||
val pointerTr = translateExpression(arrayIx.pointerderef!!)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
if(arrayVar.type.isPointer) {
|
||||
val pointerTr = translateExpression(arrayVar)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
|
||||
require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
val arrayVarSymbol = arrayVar.name
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
@@ -577,8 +374,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
|
||||
}
|
||||
|
||||
fun indexByNumber(index: Int) {
|
||||
val memOffset = index * eltSize
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
@@ -587,61 +385,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
}
|
||||
}
|
||||
|
||||
fun indexByExpression() {
|
||||
val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
|
||||
result += code
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber)
|
||||
indexByNumber((arrayIx.index as PtNumber).number.toInt())
|
||||
else
|
||||
indexByExpression()
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translatePointerIndexing(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
pointerReg: Int,
|
||||
index: PtExpression,
|
||||
eltSize: Int,
|
||||
resultDt: IRDataType
|
||||
): ExpressionCodeResult {
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
|
||||
if(index is PtNumber) {
|
||||
val memOffset = eltSize * index.number.toInt()
|
||||
if(memOffset>0)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
|
||||
}
|
||||
else {
|
||||
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
|
||||
}
|
||||
|
||||
if(resultDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(resultDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translate(expr: PtPrefix): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = translateExpression(expr.value)
|
||||
@@ -682,9 +442,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
valueDt.isWord -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isFloat -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -693,7 +453,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.UBYTE -> {
|
||||
@@ -709,7 +469,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
@@ -725,7 +485,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
@@ -747,10 +507,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -772,7 +529,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
@@ -790,61 +547,49 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
require(valueDt.isUnsignedWord || valueDt.isPointer)
|
||||
actualResultReg2 = tr.resultReg
|
||||
// no further conversion required, pointers are all just uwords
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
val vmDt = irType(binExpr.left.type)
|
||||
val signed = binExpr.left.type.isSigned
|
||||
if(binExpr.operator==".") {
|
||||
return operatorDereference(binExpr) // eww, nasty, would rather not have any such expressions anymore
|
||||
} else {
|
||||
val vmDt = irType(binExpr.left.type)
|
||||
return when (binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
"^", "xor" -> operatorXor(binExpr, vmDt)
|
||||
"or" -> operatorOr(binExpr, vmDt, false)
|
||||
"and" -> operatorAnd(binExpr, vmDt, false)
|
||||
"<<" -> operatorShiftLeft(binExpr, vmDt)
|
||||
">>" -> operatorShiftRight(binExpr, vmDt, signed)
|
||||
"==" -> operatorEquals(binExpr, vmDt, false)
|
||||
"!=" -> operatorEquals(binExpr, vmDt, true)
|
||||
"<" -> operatorLessThan(binExpr, vmDt, signed, false)
|
||||
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
|
||||
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
|
||||
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
}
|
||||
return when(binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
"^", "xor" -> operatorXor(binExpr, vmDt)
|
||||
"or" -> operatorOr(binExpr, vmDt, false)
|
||||
"and" -> operatorAnd(binExpr, vmDt, false)
|
||||
"<<" -> operatorShiftLeft(binExpr, vmDt)
|
||||
">>" -> operatorShiftRight(binExpr, vmDt, signed)
|
||||
"==" -> operatorEquals(binExpr, vmDt, false)
|
||||
"!=" -> operatorEquals(binExpr, vmDt, true)
|
||||
"<" -> operatorLessThan(binExpr, vmDt, signed, false)
|
||||
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
|
||||
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
|
||||
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
|
||||
|
||||
if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
|
||||
return translateStackFunctions(fcall, callTarget)
|
||||
}
|
||||
when(callTarget.scopedNameString) {
|
||||
when(callTarget.scopedName) {
|
||||
"sys.clear_carry" -> {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(chunk, IRInstruction(Opcode.CLC), null)
|
||||
@@ -893,7 +638,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// return value(s)
|
||||
// TODO: for current implementation of the call convention in case of multiple return values,
|
||||
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
|
||||
// So we use an empty list to avoid confusion here. This may change in a future version.
|
||||
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
|
||||
@@ -1024,9 +769,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
||||
}
|
||||
is StStruct -> {
|
||||
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
|
||||
}
|
||||
else -> {
|
||||
if(callTarget.type == StNodeType.LABEL) {
|
||||
require(fcall.void)
|
||||
@@ -1036,7 +778,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
|
||||
throw AssemblyError("invalid node type")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1069,7 +811,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
when(callTarget.scopedNameString) {
|
||||
when(callTarget.scopedName) {
|
||||
"sys.push" -> {
|
||||
// push byte
|
||||
val tr = translateExpression(fcall.args.single())
|
||||
@@ -1496,10 +1238,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, if(dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@@ -1514,10 +1256,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -1525,10 +1267,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
@@ -1696,136 +1438,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDereference(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
val left = binExpr.left as? PtArrayIndexer
|
||||
val right = binExpr.right as? PtIdentifier
|
||||
require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val field: Pair<DataType, UByte>
|
||||
val pointerReg: Int
|
||||
var extraFieldOffset = 0
|
||||
|
||||
if(left.type.isStructInstance) {
|
||||
|
||||
// indexing on a pointer directly
|
||||
// fetch pointer address, determine struct and field, add index * structsize
|
||||
if(left.variable!=null) {
|
||||
val pointerTr = translateExpression(left.variable!!)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
} else if(left.pointerderef!=null) {
|
||||
TODO("get pointer from deref $left")
|
||||
} else {
|
||||
throw AssemblyError("weird arrayindexer $left")
|
||||
}
|
||||
val struct = left.type.subType!! as StStruct
|
||||
val constindex = left.index as? PtNumber
|
||||
if(constindex!=null) {
|
||||
extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
|
||||
} else {
|
||||
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
|
||||
result += chunks
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
|
||||
}
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
|
||||
} else {
|
||||
|
||||
// indexing on an array with pointers
|
||||
// fetch the pointer from the array, determine the struct & field
|
||||
val indexedTr = translateExpression(left)
|
||||
result += indexedTr.chunks
|
||||
pointerReg = indexedTr.resultReg
|
||||
val struct = left.type.dereference().subType as? StStruct
|
||||
require(indexedTr.dt == IRDataType.WORD && struct != null)
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
}
|
||||
|
||||
// add field offset to pointer and load the value into the result register
|
||||
val fieldVmDt = irType(field.first)
|
||||
val fieldOffset = field.second.toInt() + extraFieldOffset
|
||||
var resultFpReg = -1
|
||||
var resultReg = -1
|
||||
if (fieldVmDt == IRDataType.FLOAT)
|
||||
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
else
|
||||
resultReg = codeGen.registers.next(fieldVmDt)
|
||||
if(fieldOffset==0) {
|
||||
// no offset, can use current pointer directly
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += if (fieldVmDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
|
||||
}
|
||||
} else {
|
||||
// actually have to add an offset
|
||||
if(fieldOffset<=255) {
|
||||
if(fieldVmDt==IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, fpReg1 = resultFpReg, reg1 = pointerReg, immediate = fieldOffset), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, reg1 = resultReg, reg2 = pointerReg, immediate = fieldOffset), null)
|
||||
} else {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldOffset)
|
||||
it += if (fieldVmDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, fieldVmDt, resultReg, resultFpReg)
|
||||
}
|
||||
|
||||
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UByte> {
|
||||
// returns instructions to calculate the pointer address, and the offset into the struct it points to
|
||||
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(targetPointerDeref.chain.isEmpty())
|
||||
return result to 0u // nothing to do; there's no deref chain
|
||||
|
||||
var struct: StStruct? = null
|
||||
if(targetPointerDeref.startpointer.type.subType!=null)
|
||||
struct = targetPointerDeref.startpointer.type.subType as StStruct
|
||||
if(targetPointerDeref.chain.isNotEmpty()) {
|
||||
// traverse deref chain
|
||||
for(deref in targetPointerDeref.chain.dropLast(1)) {
|
||||
val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
|
||||
struct = fieldinfo.first.subType as StStruct
|
||||
// get new pointer from field
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val field = targetPointerDeref.chain.last()
|
||||
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
|
||||
if(fieldinfo.second>0u) {
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// add the field offset
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
return result to 0u
|
||||
} else {
|
||||
return result to fieldinfo.second
|
||||
}
|
||||
}
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
}
|
||||
return result to 0u
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -56,8 +56,6 @@ class IRCodeGen(
|
||||
return irProg
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
|
||||
// Normally, block level (global) variables that have a numeric initialization value
|
||||
// are initialized via an assignment statement.
|
||||
@@ -67,9 +65,10 @@ class IRCodeGen(
|
||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||
val block = variable.parent.astNode as PtBlock
|
||||
val initialization = (block.children.firstOrNull {
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
|
||||
} as PtAssignment?)
|
||||
when(val initValue = initialization?.value){
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||
@@ -105,15 +104,6 @@ class IRCodeGen(
|
||||
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
|
||||
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
|
||||
is PtIdentifier -> {
|
||||
if('.' !in node.name) {
|
||||
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
|
||||
val expr = node.parent as? PtBinaryExpression
|
||||
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || (!expr.left.type.isPointer && !expr.left.type.isStructInstance))
|
||||
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
}
|
||||
}
|
||||
else -> { /* node has no name or is ok to have no dots in the name */ }
|
||||
}
|
||||
node.children.forEach { verifyPtNode(it) }
|
||||
@@ -267,10 +257,8 @@ class IRCodeGen(
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node $node")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
|
||||
is PtStructDecl -> emptyList()
|
||||
is PtSubSignature -> emptyList()
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@@ -336,7 +324,7 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
private fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
if(label!=null)
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
@@ -489,7 +477,7 @@ class IRCodeGen(
|
||||
translateForInNonConstantRange(forLoop, loopvar)
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
@@ -513,7 +501,7 @@ class IRCodeGen(
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
}
|
||||
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
|
||||
iterable.type.isSplitWordArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -569,7 +557,7 @@ class IRCodeGen(
|
||||
val step = iterable.step.number.toInt()
|
||||
if (step==0)
|
||||
throw AssemblyError("step 0")
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -655,7 +643,7 @@ class IRCodeGen(
|
||||
|
||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
|
||||
val loopLabel = createLabelName()
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -1677,7 +1665,7 @@ class IRCodeGen(
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtTypeCast -> {
|
||||
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
|
||||
require(cond.type.isBool && cond.value.type.isNumeric)
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
|
||||
@@ -1693,9 +1681,6 @@ class IRCodeGen(
|
||||
is PtBinaryExpression -> {
|
||||
translateBinExpr(cond)
|
||||
}
|
||||
is PtPointerDeref -> {
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
@@ -1770,7 +1755,9 @@ class IRCodeGen(
|
||||
private fun isIndirectJump(jump: PtJump): Boolean {
|
||||
if(jump.target.asConstInteger()!=null)
|
||||
return false
|
||||
val identifier = jump.target as? PtIdentifier ?: return true
|
||||
val identifier = jump.target as? PtIdentifier
|
||||
if(identifier==null)
|
||||
return true
|
||||
val symbol = symbolTable.lookup(identifier.name)
|
||||
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
|
||||
}
|
||||
@@ -1861,7 +1848,7 @@ class IRCodeGen(
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
@@ -1910,22 +1897,21 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
|
||||
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
|
||||
val result = mutableListOf<IRSubroutine.IRParam>()
|
||||
parameters.forEach {
|
||||
it as PtSubroutineParameter
|
||||
if(it.register==null) {
|
||||
require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
|
||||
val orig = symbolTable.lookup(it.name) as? StStaticVariable
|
||||
?: TODO("fix missing lookup for: ${it.name} parameter")
|
||||
result += IRSubroutine.IRParam(it.name, orig.dt)
|
||||
val flattenedName = it.definingISub()!!.name + "." + it.name
|
||||
if (symbolTable.lookup(flattenedName) == null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
result += IRSubroutine.IRParam(flattenedName, orig.dt)
|
||||
} else {
|
||||
val reg = it.register
|
||||
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||
@@ -1950,7 +1936,7 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
val args = params.map { (dt, reg)->
|
||||
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
|
||||
@@ -1961,7 +1947,9 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
when(registerOrFlag.registerOrPair) {
|
||||
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
|
||||
@@ -1984,77 +1972,4 @@ class IRCodeGen(
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UByte> {
|
||||
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
|
||||
val pointerTr = expressionEval.translateExpression(deref.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
result += instructions
|
||||
return pointerTr.resultReg to offset
|
||||
}
|
||||
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UByte, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
|
||||
if(offset<=0u) {
|
||||
val irdt = irType(type)
|
||||
val instr = if(type.isFloat) {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
|
||||
} else {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
|
||||
}
|
||||
addInstr(result, instr, null)
|
||||
return
|
||||
}
|
||||
|
||||
// store with field offset
|
||||
var valueRegister = existingValueRegister
|
||||
val irdt = irType(type)
|
||||
if(valueIsZero && valueRegister<0) {
|
||||
if(type.isFloat) {
|
||||
valueRegister = registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
|
||||
} else {
|
||||
valueRegister = registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
|
||||
}
|
||||
}
|
||||
val instr = if (type.isFloat)
|
||||
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
|
||||
addInstr(result, instr, null)
|
||||
}
|
||||
|
||||
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(wordIndex) {
|
||||
val tr = expressionEval.translateExpression(index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
var indexReg = tr.resultReg
|
||||
if(tr.dt==IRDataType.BYTE) {
|
||||
indexReg = registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
|
||||
}
|
||||
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
|
||||
return Pair(result, indexReg)
|
||||
}
|
||||
|
||||
// regular byte size index value.
|
||||
val byteIndexTr = expressionEval.translateExpression(index)
|
||||
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
|
||||
|
||||
if(itemsize==1 || arrayIsSplitWords)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
|
||||
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -53,8 +53,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
|| simplifyConstantReturns(chunk1, indexedInstructions)
|
||||
|| removeNeedlessLoads(chunk1, indexedInstructions)
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
@@ -354,29 +352,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeNeedlessLoads(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
/*
|
||||
load.b r2,#2
|
||||
loadr.b r1,r2
|
||||
jump p8_label_gen_2
|
||||
*/
|
||||
var changed=false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>=2 && ins.opcode in OpcodesThatJump) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
val previous2 = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
|
||||
if(previous.reg2==previous2.reg1) {
|
||||
chunk.instructions[idx-2] = previous2.copy(reg1=previous.reg1)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||
var changed = false
|
||||
@@ -480,23 +455,4 @@ jump p8_label_gen_2
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun simplifyConstantReturns(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// use a RETURNI when a RETURNR is just returning a constant that was loaded into a register just before
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(ins.opcode==Opcode.RETURNR) {
|
||||
if(idx>0) {
|
||||
val insBefore = chunk.instructions[idx-1]
|
||||
if(insBefore.opcode == Opcode.LOAD && insBefore.immediate!=null) {
|
||||
val constvalue = insBefore.immediate!!
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.RETURNI, ins.type, immediate = constvalue)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
@@ -27,9 +27,6 @@ class IRUnusedCodeRemover(
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
|
||||
val prefix = "$blockLabel."
|
||||
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||
|
||||
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
|
||||
|
||||
blockVars.forEach { stVar ->
|
||||
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.forEach { ins ->
|
||||
@@ -50,19 +47,6 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
}
|
||||
|
||||
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
|
||||
blockStructs.forEach { struct ->
|
||||
irprog.st.allStructInstances().forEach { instance ->
|
||||
if(instance.structName == struct.name)
|
||||
return // a struct instance is declared using this struct type
|
||||
}
|
||||
irprog.st.allVariables().forEach { variable ->
|
||||
if(variable.dt.isPointer || variable.dt.isStructInstance)
|
||||
if(struct.name == variable.dt.subType!!.scopedNameString)
|
||||
return // a variable exists with the struct as (pointer) type
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
removeBlockInits(irprog, blockLabel)
|
||||
}
|
||||
|
@@ -14,12 +14,6 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
StNodeType.STRUCTINSTANCE -> {
|
||||
val instance = it.value as StStructInstance
|
||||
val struct = sourceSt.lookup(instance.structName) as StStruct
|
||||
st.add(convert(instance, struct.fields))
|
||||
}
|
||||
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
@@ -43,18 +37,13 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
variable.dt,
|
||||
@@ -73,14 +62,14 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedName))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
val scopedName = variable.scopedNameString
|
||||
val scopedName = variable.scopedName
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
@@ -105,7 +94,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
|
||||
)
|
||||
} else {
|
||||
val scopedName = try {
|
||||
variable.scopedNameString
|
||||
variable.scopedName
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
@@ -120,7 +109,7 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedNameString
|
||||
constant.scopedName
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
@@ -133,17 +122,12 @@ private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
else
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
|
||||
}
|
||||
|
||||
|
||||
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
|
||||
val values = fields.zip(instance.initialValues).map { (field, value) ->
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
}
|
||||
/*
|
||||
|
||||
|
||||
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?
|
||||
|
||||
|
||||
*/
|
@@ -3,9 +3,7 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -6,7 +8,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":compilerAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
|
@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null) {
|
||||
if(addrOf.identifier?.inferType(program)?.isBytes==true)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
|
||||
if(addrOf.identifier.inferType(program).isBytes)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@@ -332,19 +332,15 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
}
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
}
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("constant fold pointer[i]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,8 +390,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||
require(loopvar.datatype.isBasic)
|
||||
when(val loopvarSimpleDt = loopvar.datatype.base) {
|
||||
require(loopvar.datatype.sub == null)
|
||||
val loopvarSimpleDt = loopvar.datatype.base
|
||||
when(loopvarSimpleDt) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(rangeFrom.type != BaseDataType.UBYTE) {
|
||||
// attempt to translate the iterable into ubyte values
|
||||
|
@@ -323,7 +323,7 @@ internal class ConstantIdentifierReplacer(
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
|
@@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null && typecast.type.isBasic) {
|
||||
val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
if (typecast.expression.inferType(program) issimpletype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
@@ -242,16 +242,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -264,16 +262,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -427,31 +423,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
if((arrayIndexedExpression.parent as? BinaryExpression)?.operator !=".") {
|
||||
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
val ptrDeref = arrayIndexedExpression.pointerderef
|
||||
if(ptrDeref!=null) {
|
||||
val dt = ptrDeref.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// ptr1.ptr2[0] --> ptr1.ptr2^^
|
||||
val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
// NOTE: only when the terms are not function calls!!!
|
||||
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
|
||||
@@ -528,7 +499,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (valueDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
@@ -545,7 +516,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (argDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -584,7 +555,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
|
||||
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
|
||||
// just cast the lsb to uword
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -740,9 +711,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// just use: msb(value) as type
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt.isSignedWord)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
@@ -868,14 +839,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -916,12 +887,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -932,12 +903,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount == 8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
}
|
||||
else if(amount > 8) {
|
||||
// same as above but with residual shifts. Take care to do signed shift.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
|
@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||
identifier.targetStatement()?.let { target ->
|
||||
identifier.targetStatement(program)?.let { target ->
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
when (it) {
|
||||
is NumericLiteral -> it.copy()
|
||||
is IdentifierReference -> {
|
||||
val target = it.targetStatement() ?: return emptyList()
|
||||
val target = it.targetStatement(program) ?: return emptyList()
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
IdentifierReference(scoped, it.position)
|
||||
}
|
||||
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||
return if(sub==null || !canInline(sub, functionCallStatement))
|
||||
noModifications
|
||||
else
|
||||
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
|
||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||
"invalid inline sub at ${sub.position}"
|
||||
@@ -243,13 +243,13 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
|
||||
if (!sub.inline)
|
||||
if(!sub.inline)
|
||||
return false
|
||||
if (options.compTarget.name != VMTarget.NAME) {
|
||||
if(options.compTarget.name!=VMTarget.NAME) {
|
||||
val stmt = sub.statements.single()
|
||||
if (stmt is IFunctionCall) {
|
||||
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
|
||||
return existing !is VarDecl && existing !is StructFieldRef
|
||||
return existing !is VarDecl
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@@ -29,14 +29,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
if(arg.arrayIndex==null) {
|
||||
if(arg.identifier!=null)
|
||||
arg.identifier
|
||||
else
|
||||
null // struct can't have string fields so nothing to look at here
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if(arg.arrayIndex==null) arg.identifier else null
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
@@ -153,7 +146,7 @@ class StatementOptimizer(private val program: Program,
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@@ -168,7 +161,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@@ -181,7 +174,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@@ -348,39 +341,6 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// pointer arithmetic for 6502 target
|
||||
if (options.compTarget.cpu != CpuType.VIRTUAL) {
|
||||
if(bexpr.operator=="+" && !bexpr.right.isSimple && !assignment.isAugmentable) {
|
||||
if(targetIDt.isUnsignedWord || targetIDt.getOrUndef().isPointerToByte) {
|
||||
val leftDt = bexpr.left.inferType(program).getOrUndef()
|
||||
val rightDt = bexpr.right.inferType(program).getOrUndef()
|
||||
|
||||
fun setSizedValue(a: Assignment, value: Expression, size: Int) {
|
||||
val sized = BinaryExpression(value, "*", NumericLiteral.optimalInteger(size, value.position), value.position)
|
||||
a.value = sized
|
||||
sized.linkParents(a)
|
||||
}
|
||||
|
||||
if (leftDt.isPointer && !leftDt.isPointerToByte) {
|
||||
// uword x = pointer + value --> x=value * sizeof , x += pointer
|
||||
val size = leftDt.size(options.compTarget)
|
||||
setSizedValue(assignment, bexpr.right, size)
|
||||
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position)
|
||||
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
|
||||
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
|
||||
} else if (rightDt.isPointer && !rightDt.isPointerToByte) {
|
||||
// uword x = value + pointer --> x=value * sizeof, x += pointer
|
||||
val size = rightDt.size(options.compTarget)
|
||||
setSizedValue(assignment, bexpr.left, size)
|
||||
assignment.linkParents(parent)
|
||||
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position)
|
||||
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
|
||||
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// word = lsb(word)
|
||||
|
@@ -181,20 +181,14 @@ class UnusedCodeRemover(private val program: Program,
|
||||
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
|
||||
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
|
||||
if(declIndex==singleUseIndex-1) {
|
||||
val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
|
||||
if(callStruct!=null) {
|
||||
// don't turn a struct instance allocation call to a void call, instead, remove everything
|
||||
return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
|
||||
} else {
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
@@ -16,15 +18,18 @@ dependencies {
|
||||
implementation(project(":codeGenExperimental"))
|
||||
implementation(project(":virtualmachine"))
|
||||
// implementation(project(":beanshell"))
|
||||
implementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation(project(":codeCore"))
|
||||
testImplementation(project(":intermediate"))
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
@@ -78,8 +83,6 @@ gversion {
|
||||
classPackage = "prog8.buildversion"
|
||||
className = "Version"
|
||||
language = "kotlin"
|
||||
debug = false
|
||||
annotate = true
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
|
@@ -13,7 +13,9 @@
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.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" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
@@ -23,6 +25,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -440,7 +440,6 @@ sys {
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@@ -680,7 +679,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -16,6 +16,26 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
chrout(7)
|
||||
}
|
||||
|
@@ -149,29 +149,6 @@ copy_float .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
copy_float2 .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W2,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
; clobbers X
|
||||
|
@@ -57,7 +57,7 @@ cbm {
|
||||
|
||||
; ---- CBM ROM kernal routines (C64 addresses) ----
|
||||
|
||||
extsub $AB1E = STROUT(str strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
extsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
extsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
extsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
extsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
@@ -445,7 +445,6 @@ sys {
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@@ -687,7 +686,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -15,6 +15,25 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
; beep
|
||||
|
@@ -19,9 +19,9 @@ bmx {
|
||||
uword palette_entries ; 1-256
|
||||
ubyte palette_start
|
||||
ubyte compression
|
||||
^^ubyte @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
|
||||
^^ubyte error_message ; pointer to error message, or 0 if all ok
|
||||
uword error_message ; pointer to error message, or 0 if all ok
|
||||
ubyte old_drivenumber
|
||||
|
||||
sub open(ubyte drivenumber, str filename) -> bool {
|
||||
|
@@ -121,7 +121,7 @@ io_error:
|
||||
goto diskio.directory.internal_dir
|
||||
}
|
||||
|
||||
sub diskname() -> str {
|
||||
sub diskname() -> uword {
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -165,7 +165,7 @@ io_error:
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
bool list_skip_disk_name
|
||||
^^ubyte list_pattern
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
bool iteration_in_progress = false
|
||||
bool write_iteration_in_progress = false
|
||||
@@ -174,7 +174,7 @@ io_error:
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_filenames(str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
@@ -209,7 +209,7 @@ io_error:
|
||||
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(str pattern_ptr) -> bool {
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -239,7 +239,7 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_start_list_dirs(str pattern_ptr) -> bool {
|
||||
sub lf_start_list_dirs(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version it only returns directory entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -247,7 +247,7 @@ io_error:
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
sub lf_start_list_files(str pattern_ptr) -> bool {
|
||||
sub lf_start_list_files(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version only returns actual file entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -267,7 +267,7 @@ io_error:
|
||||
repeat {
|
||||
reset_read_channel() ; use the input io channel again
|
||||
|
||||
^^ubyte nameptr = &list_filename
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
@@ -460,7 +460,7 @@ byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
||||
return total_read
|
||||
}
|
||||
|
||||
asmsub f_readline(^^ubyte bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
@@ -590,7 +590,7 @@ return_status:
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status() -> str {
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
|
||||
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
|
||||
@@ -601,7 +601,7 @@ return_status:
|
||||
; return device_not_present_error
|
||||
; }
|
||||
|
||||
^^ubyte messageptr = &list_filename
|
||||
uword messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN() ; open 15,8,15
|
||||
@@ -663,16 +663,16 @@ io_error:
|
||||
|
||||
|
||||
; saves a block of memory to disk, including the default 2 byte prg header.
|
||||
sub save(str filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, false)
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(str filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = startaddress + savesize
|
||||
@@ -719,25 +719,25 @@ io_error:
|
||||
; the return address will still be one higher. Which means, because the Kernal
|
||||
; load routine is bank-aware, it will return $a000 and will have switched to the next hiram bank!
|
||||
; So you'll have to reset the ram bank with cx16.rambank() to switch back to the bank that the data was put in.
|
||||
sub load(str filenameptr, uword address_override) -> uword {
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
return internal_load_routine(filenameptr, address_override, false)
|
||||
}
|
||||
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details. Including the banking behavior on the X16.
|
||||
sub load_raw(str filenameptr, uword startaddress) -> uword {
|
||||
sub load_raw(uword filenameptr, uword startaddress) -> uword {
|
||||
return internal_load_routine(filenameptr, startaddress, true)
|
||||
}
|
||||
|
||||
|
||||
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||
sub loadlib(str libnameptr, uword libaddress) -> uword {
|
||||
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||
return internal_load_routine(libnameptr, libaddress, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(str filenameptr, uword address_override, bool headerless) -> uword {
|
||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
@@ -760,7 +760,7 @@ io_error:
|
||||
return cx16.r1
|
||||
}
|
||||
|
||||
sub delete(str filenameptr) {
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
list_filename[1] = ':'
|
||||
@@ -772,7 +772,7 @@ io_error:
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(str oldfileptr, str newfileptr) {
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = ':'
|
||||
@@ -786,7 +786,7 @@ io_error:
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub send_command(str commandptr) {
|
||||
sub send_command(uword commandptr) {
|
||||
; -- send a dos command to the drive (don't read any response)
|
||||
cbm.SETNAM(strings.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
@@ -906,7 +906,7 @@ internal_vload:
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub curdir() -> str {
|
||||
sub curdir() -> uword {
|
||||
; return current directory name or 0 if error
|
||||
; special X16 dos command to only return the current path in the entry list (R42+)
|
||||
const ubyte MAX_PATH_LEN=80
|
||||
|
@@ -21,8 +21,8 @@ extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo'
|
||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
extsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
extsub $fe06 = FOUT() clobbers(X) -> str @ AY ; fac1 -> string, address returned in AY
|
||||
extsub $fe09 = VAL_1(str string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
|
||||
extsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
extsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
|
||||
|
||||
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
|
@@ -825,7 +825,7 @@ set_byte:
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
}
|
||||
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str sctextptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
|
@@ -743,7 +743,7 @@ skip:
|
||||
const ubyte charset_bank = $1
|
||||
const uword charset_addr = $f000 ; in bank 1, so $1f000
|
||||
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str textptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword textptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in an encoding approprite for the charset.
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
|
@@ -89,18 +89,18 @@ sprites {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
tovera(xpositions_ptr)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr+num_sprites)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
sprite_reg += 2
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
tovera(ypositions_ptr)
|
||||
|
||||
sub tovera(uword positions @R0) {
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(cx16.r0)
|
||||
cx16.VERA_DATA1 = @(cx16.r0+num_sprites)
|
||||
cx16.r0 ++
|
||||
}
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(ypositions_ptr)
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr+num_sprites)
|
||||
ypositions_ptr ++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,19 +109,20 @@ sprites {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
tovera(xpositions_ptr)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
sprite_reg += 2
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
tovera(ypositions_ptr)
|
||||
|
||||
sub tovera(uword positions @R0) {
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(cx16.r0)
|
||||
cx16.r0 ++
|
||||
cx16.VERA_DATA1 = @(cx16.r0)
|
||||
cx16.r0 ++
|
||||
}
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1470,7 +1470,7 @@ sub search_x16edit() -> ubyte {
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(str args_ptr, ubyte args_size) {
|
||||
sub set_program_args(uword args_ptr, ubyte args_size) {
|
||||
; -- Set the inter-program arguments.
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0
|
||||
@@ -1482,7 +1482,7 @@ sub search_x16edit() -> ubyte {
|
||||
rambank(sys.pop())
|
||||
}
|
||||
|
||||
asmsub get_program_args(^^ubyte buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
|
||||
asmsub get_program_args(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
|
||||
; -- Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero).
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0
|
||||
@@ -1560,7 +1560,6 @@ sys {
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@@ -1759,7 +1758,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -17,6 +17,26 @@ const long VERA_TEXTMATRIX = $1b000
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
chrout(7)
|
||||
}
|
||||
|
@@ -119,7 +119,6 @@ verafx {
|
||||
; Returns the 16 bits unsigned result of R0*R1 in AY.
|
||||
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
|
||||
; Verafx doesn't support unsigned values like this for full 32 bit result.
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -137,7 +136,6 @@ verafx {
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
|
||||
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
|
||||
; Vera Fx multiplication support only works on signed values!
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda #(2 << 1)
|
||||
sta cx16.VERA_CTRL ; $9F25
|
||||
|
@@ -6,7 +6,7 @@ cx16logo {
|
||||
%option ignore_unused
|
||||
|
||||
sub logo_at(ubyte column, ubyte row) {
|
||||
^^ubyte strptr
|
||||
uword strptr
|
||||
for strptr in logo_lines {
|
||||
txt.plot(column, row)
|
||||
txt.print(strptr)
|
||||
@@ -15,7 +15,7 @@ cx16logo {
|
||||
}
|
||||
|
||||
sub logo() {
|
||||
^^ubyte strptr
|
||||
uword strptr
|
||||
for strptr in logo_lines {
|
||||
txt.print(strptr)
|
||||
txt.nl()
|
||||
|
@@ -547,7 +547,7 @@ log2_tab
|
||||
}}
|
||||
}
|
||||
|
||||
sub crc16(^^ubyte data, uword length) -> uword {
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
; calculates the CRC16 (XMODEM) checksum of the buffer.
|
||||
; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc16 for data that doesn't fit in a single memory block.
|
||||
crc16_start()
|
||||
@@ -602,7 +602,7 @@ log2_tab
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(uword data, uword length) {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
|
@@ -104,7 +104,6 @@ sys {
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@@ -163,7 +162,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -15,6 +15,26 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
chrout(7)
|
||||
}
|
||||
@@ -205,16 +225,20 @@ sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
stx $d8
|
||||
txa
|
||||
asl a
|
||||
tax
|
||||
lda setchr._screenrows,x
|
||||
sta $c4
|
||||
lda setchr._screenrows+1,x
|
||||
sta $c5
|
||||
sty $c6
|
||||
rts
|
||||
jsr home
|
||||
cpy #0
|
||||
beq +
|
||||
- lda #29
|
||||
jsr chrout
|
||||
dey
|
||||
bne -
|
||||
+ cpx #0
|
||||
beq +
|
||||
- lda #17
|
||||
jsr chrout
|
||||
dex
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
@@ -401,17 +401,8 @@ _loop_hi ldy _index_first
|
||||
.pend
|
||||
|
||||
|
||||
func_peek .proc
|
||||
; -- read the byte value on the address in AY, into A
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_peekw .proc
|
||||
; -- read the word value on the address in AY, into AY
|
||||
; -- read the word value on the address in AY
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
|
@@ -101,7 +101,7 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
sub diskname() -> str {
|
||||
sub diskname() -> uword {
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -145,7 +145,7 @@ io_error:
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
bool list_skip_disk_name
|
||||
^^ubyte list_pattern
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
bool iteration_in_progress = false
|
||||
bool write_iteration_in_progress = false
|
||||
@@ -154,7 +154,7 @@ io_error:
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_filenames(str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
@@ -163,7 +163,7 @@ io_error:
|
||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
||||
; and registering when the next one starts after the 0-byte separator.
|
||||
uword buffer_start = filenames_buffer
|
||||
uword buffer_start = filenames_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(pattern_ptr) {
|
||||
while lf_next_entry() {
|
||||
@@ -187,7 +187,7 @@ io_error:
|
||||
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(str pattern_ptr) -> bool {
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -217,7 +217,7 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_start_list_dirs(str pattern_ptr) -> bool {
|
||||
sub lf_start_list_dirs(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version it only returns directory entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -225,7 +225,7 @@ io_error:
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
sub lf_start_list_files(str pattern_ptr) -> bool {
|
||||
sub lf_start_list_files(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version only returns actual file entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -244,7 +244,7 @@ io_error:
|
||||
repeat {
|
||||
reset_read_channel() ; use the input io channel again
|
||||
|
||||
^^ubyte nameptr = &list_filename
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
@@ -311,7 +311,7 @@ close_end:
|
||||
|
||||
; ----- iterative file loader functions (uses the input io channel) -----
|
||||
|
||||
sub f_open(str filenameptr) -> bool {
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
; Returns true if the file is successfully opened and readable.
|
||||
@@ -413,8 +413,8 @@ close_end:
|
||||
return total_read
|
||||
}
|
||||
|
||||
asmsub f_readline(^^ubyte bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
; Routine to read a text line from a text file. Lines must be less than 255 characters.
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
|
||||
@@ -461,7 +461,7 @@ _end jsr cbm.READST
|
||||
|
||||
; ----- iterative file writing functions (uses write io channel) -----
|
||||
|
||||
sub f_open_w(str filenameptr) -> bool {
|
||||
sub f_open_w(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative writing with f_write
|
||||
; WARNING: returns true if the open command was received by the device,
|
||||
; but this can still mean the file wasn't successfully opened for writing!
|
||||
@@ -516,7 +516,7 @@ _end jsr cbm.READST
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status() -> str {
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
|
||||
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
|
||||
@@ -527,7 +527,7 @@ _end jsr cbm.READST
|
||||
; return device_not_present_error
|
||||
; }
|
||||
|
||||
^^ubyte messageptr = &list_filename
|
||||
uword messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN() ; open 15,8,15
|
||||
@@ -586,7 +586,7 @@ io_error:
|
||||
return 255
|
||||
}
|
||||
|
||||
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
|
||||
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = start_address + savesize
|
||||
@@ -617,7 +617,7 @@ io_error:
|
||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||
; and the rest is loaded at the given location in memory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
sub load(str filenameptr, uword address_override) -> uword {
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
@@ -641,7 +641,7 @@ io_error:
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(str filenameptr, uword start_address) -> uword {
|
||||
sub load_raw(uword filenameptr, uword start_address) -> uword {
|
||||
; read the 2 header bytes separately to skip them
|
||||
if not f_open(filenameptr)
|
||||
return 0
|
||||
@@ -654,12 +654,12 @@ io_error:
|
||||
}
|
||||
|
||||
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||
sub loadlib(str libnameptr, uword libaddress) -> uword {
|
||||
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||
return load(libnameptr, libaddress)
|
||||
}
|
||||
|
||||
|
||||
sub delete(str filenameptr) {
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
list_filename[1] = ':'
|
||||
@@ -671,7 +671,7 @@ io_error:
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(str oldfileptr, str newfileptr) {
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = ':'
|
||||
@@ -696,7 +696,7 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub send_command(str commandptr) {
|
||||
sub send_command(uword commandptr) {
|
||||
; -- send a dos command to the drive
|
||||
cbm.SETNAM(strings.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
|
@@ -3,35 +3,6 @@ txt {
|
||||
|
||||
%option merge, no_symbol_prefixing, ignore_unused
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub rvs_on() {
|
||||
txt.chrout(18)
|
||||
}
|
||||
|
||||
sub rvs_off() {
|
||||
txt.chrout(146)
|
||||
}
|
||||
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print zero terminated string, in PETSCII encoding, from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
@@ -165,7 +136,7 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (^^ubyte buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
|
||||
; Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
@@ -108,7 +108,7 @@ strings {
|
||||
repeat cx16.r2L {
|
||||
if startswith(cx16.r3, needle) {
|
||||
sys.set_carry()
|
||||
return cx16.r3-(haystack as uword) as ubyte
|
||||
return cx16.r3-haystack as ubyte
|
||||
}
|
||||
cx16.r3++
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ sorting {
|
||||
; NOTE: sorting is done in ascending order!!!
|
||||
; Note: could be made slightly faster by using modifying dcode for the CPY after _loop but that compromises romability
|
||||
|
||||
asmsub gnomesort_ub(^^ubyte bytearray @AY, ubyte num_elements @X) {
|
||||
asmsub gnomesort_ub(uword bytearray @AY, ubyte num_elements @X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -43,21 +43,21 @@ _done
|
||||
}}
|
||||
}
|
||||
|
||||
sub gnomesort_by_ub(^^ubyte @requirezp ub_keys, ^^uword wordvalues, ubyte num_elements) {
|
||||
sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
ubyte @zp pos=1
|
||||
while pos != num_elements {
|
||||
if ub_keys[pos]>=ub_keys[pos-1]
|
||||
if uw_keys[pos]>=uw_keys[pos-1]
|
||||
pos++
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0L = ub_keys[pos-1]
|
||||
ub_keys[pos-1] = ub_keys[pos]
|
||||
ub_keys[pos] = cx16.r0L
|
||||
^^uword @requirezp vptr = wordvalues + (pos-1)
|
||||
cx16.r0L = uw_keys[pos-1]
|
||||
uw_keys[pos-1] = uw_keys[pos]
|
||||
uw_keys[pos] = cx16.r0L
|
||||
uword @requirezp vptr = values + pos*$0002 -2
|
||||
cx16.r0 = peekw(vptr)
|
||||
pokew(vptr, peekw(vptr+1))
|
||||
pokew(vptr+1, cx16.r0)
|
||||
pokew(vptr, peekw(vptr+2))
|
||||
pokew(vptr+2, cx16.r0)
|
||||
|
||||
pos--
|
||||
if_z
|
||||
@@ -66,20 +66,20 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub gnomesort_uw(uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; Sorts the wordvalues array (no-split unsigned words).
|
||||
; Max number of elements is 128 to keep indexing code size small and fast. Clobbers R0 and R1.
|
||||
sub gnomesort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; Sorts the values array (no-split unsigned words).
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r1L = pos-2
|
||||
if peekw(wordvalues+pos) >= peekw(wordvalues + cx16.r1L)
|
||||
if peekw(values+pos) >= peekw(values + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0 = peekw(wordvalues + cx16.r1L)
|
||||
pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
|
||||
pokew(wordvalues + pos, cx16.r0)
|
||||
cx16.r0 = peekw(values + cx16.r1L)
|
||||
pokew(values + cx16.r1L, peekw(values + pos))
|
||||
pokew(values + pos, cx16.r0)
|
||||
pos-=2
|
||||
if_z
|
||||
pos+=2
|
||||
@@ -90,7 +90,7 @@ _done
|
||||
sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) {
|
||||
; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
; Max number of elements is 128 to keep indexing code size small and fast. Clobbers R0 and R1.
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
@@ -115,7 +115,7 @@ _done
|
||||
|
||||
; gnomesort_pointers is not worth it over shellshort_pointers.
|
||||
|
||||
sub shellsort_ub(^^ubyte @requirezp values, ubyte num_elements) {
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (unsigned bytes).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
@@ -138,7 +138,6 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
; TODO convert to ^^uword once code size regression is fixed
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (no-split unsigned words).
|
||||
num_elements--
|
||||
@@ -161,8 +160,7 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
; TODO convert to ^^uword once code size regression is fixed
|
||||
sub shellsort_by_ub(^^ubyte @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
@@ -188,7 +186,6 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
; TODO convert to ^^uword once code size regression is fixed
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
@@ -239,7 +236,7 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
asmsub string_comparator(str string1 @R0, str string2 @R1) -> bool @Pc {
|
||||
asmsub string_comparator(uword string1 @R0, uword string2 @R1) -> bool @Pc {
|
||||
; R0 and R1 are the two strings, must return Carry=1 when R0<=R1, else Carry=0
|
||||
%asm {{
|
||||
lda cx16.r1L
|
||||
|
@@ -5,7 +5,7 @@
|
||||
strings {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
asmsub length(str string @AY) clobbers(A) -> ubyte @Y {
|
||||
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
|
||||
; Returns the number of bytes in the string.
|
||||
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
||||
; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof!
|
||||
@@ -23,7 +23,7 @@ strings {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub left(str source @AX, ubyte length @Y, str target @R1) clobbers(A, Y) {
|
||||
asmsub left(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) {
|
||||
; Copies the left side of the source string of the given length to target string.
|
||||
; It is assumed the target string buffer is large enough to contain the result.
|
||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||
@@ -50,7 +50,7 @@ _loop dey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub right(str source @AY, ubyte length @X, str target @R1) clobbers(A,Y) {
|
||||
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
||||
; Copies the right side of the source string of the given length to target string.
|
||||
; It is assumed the target string buffer is large enough to contain the result.
|
||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||
@@ -89,7 +89,7 @@ _loop dey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub slice(str source @R0, ubyte start @A, ubyte length @Y, str target @R1) clobbers(A, Y) {
|
||||
asmsub slice(uword source @R0, ubyte start @A, ubyte length @Y, uword target @R1) clobbers(A, Y) {
|
||||
; Copies a segment from the source string, starting at the given index,
|
||||
; and of the given length to target string.
|
||||
; It is assumed the target string buffer is large enough to contain the result.
|
||||
@@ -127,7 +127,7 @@ _startloop dey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub find(str string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
asmsub find(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string,
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
%asm {{
|
||||
@@ -152,7 +152,7 @@ _found tya
|
||||
}
|
||||
|
||||
|
||||
asmsub find_eol(str string @AY) -> ubyte @A, bool @Pc {
|
||||
asmsub find_eol(uword string @AY) -> ubyte @A, bool @Pc {
|
||||
; Locates the position of the first End Of Line character in the string.
|
||||
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
@@ -178,7 +178,7 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rfind(str string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
%asm {{
|
||||
@@ -203,14 +203,14 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub contains(str string @AY, ubyte character @X) -> bool @Pc {
|
||||
asmsub contains(uword string @AY, ubyte character @X) -> bool @Pc {
|
||||
; Just return true/false if the character is in the given string or not.
|
||||
%asm {{
|
||||
jmp find
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub copy(str source @R0, str target @AY) clobbers(A) -> ubyte @Y {
|
||||
asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y {
|
||||
; Copy a string to another, overwriting that one.
|
||||
; Returns the length of the string that was copied.
|
||||
; Often you don’t have to call this explicitly and can just write string1 = string2
|
||||
@@ -224,7 +224,7 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub append(str target @R0, str suffix @R1) clobbers(Y) -> ubyte @A {
|
||||
asmsub append(uword target @R0, uword suffix @R1) clobbers(Y) -> ubyte @A {
|
||||
; Append the suffix string to the target. (make sure the buffer is large enough!)
|
||||
; Returns the length of the resulting string.
|
||||
%asm {{
|
||||
@@ -249,7 +249,7 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub compare(str string1 @R0, str string2 @AY) clobbers(Y) -> byte @A {
|
||||
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
||||
; Compares two strings for sorting.
|
||||
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
|
||||
; Note that you can also directly compare strings and string values with eachother using
|
||||
@@ -263,7 +263,7 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ncompare(str string1 @R0, str string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A {
|
||||
asmsub ncompare(uword string1 @R0, uword string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A {
|
||||
; Compares two strings for sorting.
|
||||
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
|
||||
; Only compares the strings from index 0 up to the length argument.
|
||||
@@ -276,7 +276,7 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub lower(str st @AY) -> ubyte @Y {
|
||||
asmsub lower(uword st @AY) -> ubyte @Y {
|
||||
; Lowercases the petscii string in-place. Returns length of the string.
|
||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||
; but regular text doesn't usually contain those characters anyway.)
|
||||
@@ -299,7 +299,7 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub upper(str st @AY) -> ubyte @Y {
|
||||
asmsub upper(uword st @AY) -> ubyte @Y {
|
||||
; Uppercases the petscii string in-place. Returns length of the string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -1,6 +1,6 @@
|
||||
compression {
|
||||
|
||||
sub decode_rle(^^ubyte @zp compressed, ^^ubyte @zp target, uword maxsize) -> uword {
|
||||
sub decode_rle(uword @zp compressed, uword @zp target, uword maxsize) -> uword {
|
||||
cx16.r0 = target ; original target
|
||||
cx16.r1 = target+maxsize ; decompression limit
|
||||
|
||||
@@ -30,7 +30,7 @@ compression {
|
||||
return target-cx16.r0
|
||||
}
|
||||
|
||||
sub encode_rle(^^ubyte data, uword size, ^^ubyte target, bool is_last_block) -> uword {
|
||||
sub encode_rle(uword data, uword size, uword target, bool is_last_block) -> uword {
|
||||
; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding.
|
||||
; Returns the size of the compressed RLE data. Worst case result storage size needed = (size + (size+126) / 127) + 1.
|
||||
; is_last_block = usually true, but you can set it to false if you want to concatenate multiple
|
||||
@@ -39,7 +39,7 @@ compression {
|
||||
uword idx = 0
|
||||
uword literals_start_idx = 0
|
||||
ubyte literals_length = 0
|
||||
^^ubyte orig_target = target
|
||||
uword orig_target = target
|
||||
|
||||
sub next_same_span() {
|
||||
; returns length in cx16.r1L, and the byte value in cx16.r1H
|
||||
@@ -54,7 +54,7 @@ compression {
|
||||
sub output_literals() {
|
||||
@(target) = literals_length-1
|
||||
target++
|
||||
^^ubyte dataptr = data + literals_start_idx
|
||||
uword dataptr = data + literals_start_idx
|
||||
ubyte i
|
||||
for i in 0 to literals_length-1 {
|
||||
@(target) = @(dataptr)
|
||||
|
@@ -29,7 +29,7 @@ sub str_ub(ubyte value) -> str {
|
||||
|
||||
sub str_b(byte value) -> str {
|
||||
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||
^^ubyte out_ptr = &string_out
|
||||
uword out_ptr = &string_out
|
||||
if value<0 {
|
||||
@(out_ptr) = '-'
|
||||
out_ptr++
|
||||
@@ -39,7 +39,7 @@ sub str_b(byte value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub internal_str_ub(ubyte value, str out_ptr) {
|
||||
sub internal_str_ub(ubyte value, uword out_ptr) {
|
||||
ubyte hundreds = value / 100
|
||||
value -= hundreds*100
|
||||
ubyte tens = value / 10
|
||||
@@ -73,7 +73,7 @@ sub str_ubhex (ubyte value) -> str {
|
||||
|
||||
sub str_ubbin (ubyte value) -> str {
|
||||
; ---- convert the ubyte in A in binary string form
|
||||
^^ubyte out_ptr = &string_out
|
||||
uword out_ptr = &string_out
|
||||
repeat 8 {
|
||||
rol(value)
|
||||
if_cc
|
||||
@@ -88,7 +88,7 @@ sub str_ubbin (ubyte value) -> str {
|
||||
|
||||
sub str_uwbin (uword value) -> str {
|
||||
; ---- convert the uword in A/Y in binary string form
|
||||
^^ubyte out_ptr = &string_out
|
||||
uword out_ptr = &string_out
|
||||
repeat 16 {
|
||||
rol(value)
|
||||
if_cc
|
||||
@@ -142,7 +142,7 @@ sub str_uw (uword value) -> str {
|
||||
|
||||
sub str_w (word value) -> str {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
^^ubyte out_ptr = &string_out
|
||||
uword out_ptr = &string_out
|
||||
if value<0 {
|
||||
@(out_ptr) = '-'
|
||||
out_ptr++
|
||||
@@ -152,7 +152,7 @@ sub str_w (word value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub internal_str_uw(uword value, str out_ptr) {
|
||||
sub internal_str_uw(uword value, uword out_ptr) {
|
||||
uword value2 = value/10
|
||||
ubyte digits = value-value2*10 as ubyte
|
||||
uword value3 = value2/10
|
||||
|
@@ -24,7 +24,7 @@ diskio {
|
||||
|
||||
; ----- iterative file loader functions (uses the input io channel) -----
|
||||
|
||||
sub f_open(str filenameptr) -> bool {
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
; Returns true if the file is successfully opened and readable.
|
||||
@@ -39,7 +39,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub f_read(str bufferpointer, uword num_bytes) -> uword {
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
uword actual
|
||||
@@ -57,7 +57,7 @@ diskio {
|
||||
return actual
|
||||
}
|
||||
|
||||
sub f_read_all(str bufferpointer) -> uword {
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full rest of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
; Usually you will just be using load() / load_raw() to read entire files!
|
||||
@@ -75,7 +75,7 @@ diskio {
|
||||
}
|
||||
}
|
||||
|
||||
sub f_readline(str bufptr) -> ubyte {
|
||||
sub f_readline(uword bufptr) -> ubyte {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
@@ -119,7 +119,7 @@ diskio {
|
||||
|
||||
; ----- iterative file writing functions (uses write io channel) -----
|
||||
|
||||
sub f_open_w(str filenameptr) -> bool {
|
||||
sub f_open_w(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative writing with f_write
|
||||
; WARNING: returns true if the open command was received by the device,
|
||||
; but this can still mean the file wasn't successfully opened for writing!
|
||||
@@ -133,7 +133,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub f_write(str bufferpointer, uword num_bytes) -> bool {
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||
; -- write the given number of bytes to the currently open file
|
||||
; you can call this multiple times to append more data
|
||||
repeat num_bytes {
|
||||
@@ -185,7 +185,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub curdir() -> str {
|
||||
sub curdir() -> uword {
|
||||
; return current directory name or 0 if error
|
||||
%ir {{
|
||||
syscall 48 (): r99000.w
|
||||
@@ -204,7 +204,7 @@ diskio {
|
||||
}
|
||||
|
||||
|
||||
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
|
||||
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
%ir {{
|
||||
load.b r99100,0
|
||||
loadm.w r99000,diskio.save.filenameptr
|
||||
@@ -216,7 +216,7 @@ diskio {
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
|
||||
sub save_raw(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
%ir {{
|
||||
load.b r99100,1
|
||||
loadm.w r99000,diskio.save_raw.filenameptr
|
||||
@@ -233,7 +233,7 @@ diskio {
|
||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||
; and the rest is loaded at the given location in memory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
sub load(str filenameptr, uword address_override) -> uword {
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.load.filenameptr
|
||||
loadm.w r99001,diskio.load.address_override
|
||||
@@ -245,7 +245,7 @@ diskio {
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(str filenameptr, uword start_address) -> uword {
|
||||
sub load_raw(uword filenameptr, uword start_address) -> uword {
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.load_raw.filenameptr
|
||||
loadm.w r99001,diskio.load_raw.start_address
|
||||
@@ -254,7 +254,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub delete(str filenameptr) {
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.delete.filenameptr
|
||||
@@ -262,7 +262,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub rename(str oldfileptr, str newfileptr) {
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.rename.oldfileptr
|
||||
|
@@ -316,7 +316,7 @@ math {
|
||||
return w2-w1
|
||||
}
|
||||
|
||||
sub crc16(^^ubyte data, uword length) -> uword {
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
; calculates the CRC16 (XMODEM) checksum of the buffer.
|
||||
; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block.
|
||||
crc16_start()
|
||||
@@ -353,7 +353,7 @@ math {
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(uword data, uword length) {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
|
@@ -5,7 +5,7 @@
|
||||
sorting {
|
||||
%option ignore_unused
|
||||
|
||||
sub shellsort_ub(^^ubyte @requirezp values, ubyte num_elements) {
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
@@ -27,29 +27,29 @@ sorting {
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_uw(^^uword @requirezp values, ubyte num_elements) {
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = values[i]
|
||||
uword @zp temp = peekw(values+i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = values[k]
|
||||
uword @zp v = peekw(values+k*2)
|
||||
if v <= temp break
|
||||
values[j] = v
|
||||
pokew(values+j*2, v)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
values[j] = temp
|
||||
pokew(values+j*2, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub shellsort_by_ub(^^ubyte @requirezp ub_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
@@ -57,7 +57,7 @@ sorting {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = ub_keys[i]
|
||||
uword temp_wv = wordvalues[i]
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
@@ -65,17 +65,17 @@ sorting {
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
ub_keys[j] = v
|
||||
wordvalues[j] = wordvalues[k]
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
wordvalues[j] = temp_wv
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_uw(^^uword @requirezp uw_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
num_elements--
|
||||
@@ -83,20 +83,20 @@ sorting {
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = uw_keys[i]
|
||||
uword temp_wv = wordvalues[i]
|
||||
uword @zp temp = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = uw_keys[k]
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
if v <= temp break
|
||||
uw_keys[j] = v
|
||||
wordvalues[j] = wordvalues[k]
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
uw_keys[j] = temp
|
||||
wordvalues[j] = temp_wv
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -82,7 +82,7 @@ strings {
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(str stringptr, ubyte character) -> ubyte, bool {
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte, bool {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
|
@@ -13,7 +13,6 @@ sys {
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@@ -48,7 +47,7 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_stringcopy(str source, str tgt) {
|
||||
sub internal_stringcopy(uword source, uword tgt) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%ir {{
|
||||
loadm.w r99000,sys.internal_stringcopy.source
|
||||
|
@@ -53,21 +53,6 @@ sub uppercase() {
|
||||
; not supported
|
||||
}
|
||||
|
||||
sub rvs_on() {
|
||||
print("\x1b[7m")
|
||||
}
|
||||
|
||||
sub rvs_off() {
|
||||
print("\x1b[0m")
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
print("\x1b[3")
|
||||
chrout('0' + txtcol)
|
||||
chrout('m')
|
||||
}
|
||||
|
||||
|
||||
sub chrout(ubyte char) {
|
||||
%ir {{
|
||||
loadm.b r99100,txt.chrout.char
|
||||
@@ -151,7 +136,7 @@ sub print_w (word value) {
|
||||
print(conv.str_w(value))
|
||||
}
|
||||
|
||||
sub input_chars (str buffer) -> ubyte {
|
||||
sub input_chars (uword buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
%ir {{
|
||||
|
@@ -94,16 +94,16 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
// 1 arg, type = anything, result type = ubyte or uword
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("sizeof requires one argument", position)
|
||||
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral && args[0] !is AddressOf)
|
||||
throw CannotEvaluateException("sizeof","argument should be an identifier, number, or type name")
|
||||
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral)
|
||||
throw SyntaxError("sizeof argument should be an identifier or number", position)
|
||||
|
||||
val dt = args[0].inferType(program)
|
||||
if(dt.isKnown) {
|
||||
if(args[0] is NumericLiteral || args[0] is AddressOf)
|
||||
if(args[0] is NumericLiteral)
|
||||
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
|
||||
val target = (args[0] as? IdentifierReference)?.targetStatement()
|
||||
?: throw SyntaxError("wrong argument type", position)
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
|
||||
return when {
|
||||
dt.isArray -> {
|
||||
@@ -112,7 +112,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
|
||||
}
|
||||
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
|
||||
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
}
|
||||
} else {
|
||||
val identifier = args[0] as? IdentifierReference
|
||||
@@ -128,17 +128,11 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
}
|
||||
}
|
||||
|
||||
// the argument could refer to a struct declaration
|
||||
val struct = (args[0] as? IdentifierReference)?.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val size = struct.memsize(program.memsizer)
|
||||
return NumericLiteral(BaseDataType.UBYTE, size.toDouble(), position)
|
||||
}
|
||||
|
||||
throw SyntaxError("sizeof argument should be an identifier, number, or type name", position)
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
|
@@ -181,7 +181,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
@@ -199,7 +199,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
args.errors.report()
|
||||
intermediateAst
|
||||
}
|
||||
simplifiedAstDuration = simplifiedAstDuration2
|
||||
simplifiedAstDuration =simplifiedAstDuration2
|
||||
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
@@ -299,7 +299,7 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
|
||||
|
||||
if(loadAddress>options.memtopAddress) {
|
||||
errors.warn("program load address ${loadAddress.toHex()} is beyond default memtop address ${options.memtopAddress.toHex()}. " +
|
||||
$$"Memtop has been adjusted to $ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
|
||||
"Memtop has been adjusted to ${'$'}ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
|
||||
options.memtopAddress = 0xffffu
|
||||
}
|
||||
}
|
||||
@@ -456,6 +456,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
|
||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.preprocessAst(errors, compilerOptions)
|
||||
|
||||
if(errors.noErrors() && compilerOptions.dumpSymbols) {
|
||||
printSymbols(program)
|
||||
exitProcess(0)
|
||||
|
@@ -38,7 +38,7 @@ internal class ErrorReporter(val colors: IConsoleColors): IErrorReporter {
|
||||
// because those are very likely caused by the unknown symbol. This reduces error clutter.
|
||||
val undefinedSymbolErrors = messages
|
||||
.asSequence()
|
||||
.filter { it.severity == MessageSeverity.ERROR && (it.message.contains("undefined symbol") || it.message.contains("no such field")) }
|
||||
.filter { it.severity == MessageSeverity.ERROR && it.message.contains("undefined symbol") }
|
||||
.map { it to (it.position.file to it.position.line)}
|
||||
.groupBy { it.second }
|
||||
.map { it.value.first().first }
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -119,7 +119,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
|
||||
val lit2decl = LiteralsToAutoVars(this, errors)
|
||||
lit2decl.visit(this)
|
||||
while(errors.noErrors() && lit2decl.applyModifications()>0)
|
||||
lit2decl.visit(this)
|
||||
@@ -202,7 +202,7 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean
|
||||
}
|
||||
|
||||
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
|
||||
when (val targetStatement = targetStatement(program.builtinFunctions)) {
|
||||
when (val targetStatement = this.targetStatement(program)) {
|
||||
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
||||
is VarDecl -> {
|
||||
if(statement is Jump) {
|
||||
@@ -214,7 +214,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
|
||||
else
|
||||
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
|
||||
}
|
||||
is Alias, is StructDecl, is StructFieldRef -> {
|
||||
is Alias -> {
|
||||
return targetStatement
|
||||
}
|
||||
null -> {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user