mirror of
https://github.com/irmen/prog8.git
synced 2025-09-12 00:24:34 +00:00
Compare commits
245 Commits
v11.4
...
structs650
Author | SHA1 | Date | |
---|---|---|---|
|
aee53b14c7 | ||
|
5eb2fc8d86 | ||
|
221a093e5f | ||
|
2ca1820d4e | ||
|
d58737d5be | ||
|
b52cee3154 | ||
|
9a76941e10 | ||
|
0285a4cce1 | ||
|
5a3aa1bd25 | ||
|
0f79351de9 | ||
|
3cdb25ce8e | ||
|
b7193bd0c6 | ||
|
10ff6a0095 | ||
|
d30f58004e | ||
|
17bdb22e4f | ||
|
a68209be37 | ||
|
7b40eade44 | ||
|
8a717c74b9 | ||
|
aa72ded21e | ||
|
8e53c83844 | ||
|
e2a2db1256 | ||
|
2afd2d4dae | ||
|
fad17cc094 | ||
|
f93d957999 | ||
|
8db0344cee | ||
|
32e531f951 | ||
|
a4769702f9 | ||
|
cf19fb8df1 | ||
|
79b8bb5c9f | ||
|
fc5889ec0b | ||
|
369303c46d | ||
|
d65670cc7b | ||
|
f74eeaee0f | ||
|
826fb3e9c2 | ||
|
a3d7b8a899 | ||
|
0cc36ed6e4 | ||
|
976bd52972 | ||
|
4a8d5def84 | ||
|
2f60716082 | ||
|
729efb04e1 | ||
|
4ea8b4d445 | ||
|
e800c165f9 | ||
|
fd9bd23449 | ||
|
8880ed1393 | ||
|
f7fde070ca | ||
|
5ada80779d | ||
|
8972235a0e | ||
|
e56f533e38 | ||
|
324fb7dbf7 | ||
|
44285b9b5d | ||
|
a68f477d61 | ||
|
ae9f99448e | ||
|
7c0fb10197 | ||
|
9e85571a7b | ||
|
9e10c15e2e | ||
|
6bd7752bac | ||
|
83ec437e8a | ||
|
4a1d05dd46 | ||
|
aa324e355a | ||
|
5cb8bcead7 | ||
|
bbd06c0c99 | ||
|
651830ea82 | ||
|
c70146f1dc | ||
|
d4e83b28bb | ||
|
bc58a25765 | ||
|
38645022c9 | ||
|
647cd0fbe1 | ||
|
ea8935a346 | ||
|
7ea80babfc | ||
|
dee761a99e | ||
|
88ee7a8187 | ||
|
eb8b408b82 | ||
|
3d10882f57 | ||
|
1988496512 | ||
|
88b074c208 | ||
|
c4c5636a81 | ||
|
c39d570b72 | ||
|
4ccd7f9f3a | ||
|
1c9c5aeef7 | ||
|
23ad540aa5 | ||
|
08810c2749 | ||
|
a52966f327 | ||
|
624220e9a3 | ||
|
842b11ed9e | ||
|
82267b3f56 | ||
|
67fb45a55b | ||
|
11186f1dbe | ||
|
0116fac201 | ||
|
817f4f8e7c | ||
|
866313209b | ||
|
28e351daab | ||
|
893e16d814 | ||
|
33470c47fc | ||
|
63f7b87572 | ||
|
e2901cca1b | ||
|
ce8006992a | ||
|
0b5413ad83 | ||
|
dd7adde387 | ||
|
23058b51a1 | ||
|
2f90c53ad0 | ||
|
c3be7ab4b3 | ||
|
a9b8fbc6c6 | ||
|
720988ae72 | ||
|
b0981a5fae | ||
|
ea5deeefbd | ||
|
054c98da7c | ||
|
dc434d034a | ||
|
a4a1b563aa | ||
|
3f34b83e0d | ||
|
9c63ef39c7 | ||
|
9f6106452e | ||
|
f9fbfe30e3 | ||
|
9a9bf170c6 | ||
|
7dd64b4f13 | ||
|
b6c0bac96f | ||
|
8ede098154 | ||
|
2a4a3b786e | ||
|
b4e0a2019e | ||
|
e14c3f8b59 | ||
|
c81f76226d | ||
|
edc353cc24 | ||
|
dcce519c69 | ||
|
0a16dcafc0 | ||
|
54d41b7f6f | ||
|
0541b84d09 | ||
|
2119817e4a | ||
|
a68cf3c812 | ||
|
c2bf9024f8 | ||
|
bd72eaad4c | ||
|
1d306e5cdc | ||
|
b137164fe6 | ||
|
67d4ad50e1 | ||
|
c71066af4c | ||
|
6f0a0981bd | ||
|
49a4d9ba37 | ||
|
fcdfa741b9 | ||
|
3bab177d50 | ||
|
12abafb917 | ||
|
0be90dedf2 | ||
|
e6bab3ceeb | ||
|
59387b2ae8 | ||
|
f8f20440d3 | ||
|
f8722faa4e | ||
|
26fbbf48a4 | ||
|
d5cc414221 | ||
|
d28f154f1c | ||
|
1c02179c5c | ||
|
77584493fd | ||
|
a36709e638 | ||
|
8d63cce749 | ||
|
8e7bbcdbe0 | ||
|
37ecdc47b3 | ||
|
112ca3cc53 | ||
|
33b3a1664c | ||
|
8a0c02e264 | ||
|
e40ace9dea | ||
|
4c0e6e2640 | ||
|
08b314c37d | ||
|
86da9d3c7e | ||
|
4e61e25c02 | ||
|
5097d52d99 | ||
|
09d2185bb1 | ||
|
5c02e2bd71 | ||
|
fb01389b3d | ||
|
aaa81210ce | ||
|
51269257ea | ||
|
23a853db1e | ||
|
db6c887795 | ||
|
61fe55168a | ||
|
1c4999ec87 | ||
|
c726d3f937 | ||
|
f70341df1b | ||
|
f0b791452e | ||
|
adf5600a9b | ||
|
6d4ccc5feb | ||
|
5f3829d5cc | ||
|
770ebdcd4a | ||
|
96f690e749 | ||
|
eabdd3a8f3 | ||
|
50650b966b | ||
|
65e34d4989 | ||
|
05dad5ab5f | ||
|
1a69a2f1bc | ||
|
435faafaad | ||
|
686b32dc29 | ||
|
0e64a22910 | ||
|
4f0839f27e | ||
|
bb1953267d | ||
|
cd8aae4681 | ||
|
11456496bd | ||
|
86eef7039f | ||
|
f4b2264fcf | ||
|
9b36ae2277 | ||
|
913ab03963 | ||
|
38448e471c | ||
|
d138a7a567 | ||
|
ea27d732ab | ||
|
924e28e9b3 | ||
|
60a73248cd | ||
|
abbb7d7ba3 | ||
|
59c378089e | ||
|
0b789b5f0b | ||
|
4382b96a9a | ||
|
246e4f35a6 | ||
|
506062c6b6 | ||
|
8353c689ca | ||
|
e98e6f70ac | ||
|
134352ed7c | ||
|
5de626aab8 | ||
|
7aad5d486e | ||
|
701f155951 | ||
|
8c324d7514 | ||
|
97390db5f5 | ||
|
af920d1427 | ||
|
779ebc0537 | ||
|
38949b82c3 | ||
|
d11386ef26 | ||
|
0e0377d1f0 | ||
|
55e0dbab27 | ||
|
4dc82f2c83 | ||
|
1ba5587404 | ||
|
835c4b6da3 | ||
|
dbd955b61e | ||
|
d20e2fd88c | ||
|
e0dea89477 | ||
|
6fc2902895 | ||
|
c96e4b40d4 | ||
|
37da3e2170 | ||
|
2661d3c489 | ||
|
b89bbb9281 | ||
|
696bf636ed | ||
|
40952a788a | ||
|
0162e7a0c1 | ||
|
6ce099f176 | ||
|
476a4bac8e | ||
|
63a410a6df | ||
|
cca27faa3b | ||
|
803e6bd81a | ||
|
88269628a2 | ||
|
b920d553a0 | ||
|
5e2d0d0dfc | ||
|
2ae3bd68eb | ||
|
9c183f27eb | ||
|
8046023e82 | ||
|
e328520588 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,10 +7,13 @@ build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
|
1288
.idea/inspectionProfiles/Project_Default.xml
generated
1288
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
8
.idea/kotlinc.xml
generated
8
.idea/kotlinc.xml
generated
@@ -7,13 +7,13 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.1" />
|
||||
<option name="languageVersion" value="2.1" />
|
||||
<option name="apiVersion" value="2.2" />
|
||||
<option name="languageVersion" value="2.2" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||
<option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
<option name="version" value="2.2.0" />
|
||||
</component>
|
||||
</project>
|
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.1.20" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.10" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.10/kotlin-stdlib-jdk8-2.2.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.10/kotlin-stdlib-2.2.10.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.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.10/kotlin-stdlib-jdk7-2.2.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.10/kotlin-stdlib-jdk8-2.2.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.10/kotlin-stdlib-2.2.10-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.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.10/kotlin-stdlib-jdk7-2.2.10-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.10/kotlin-stdlib-jdk8-2.2.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.10/kotlin-stdlib-2.2.10-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.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.10/kotlin-stdlib-jdk7-2.2.10-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
@@ -1,20 +0,0 @@
|
||||
<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.0.1" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
|
||||
<CLASSES>
|
||||
<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$/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$/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.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$/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$/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.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$/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$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
|
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -26,4 +26,7 @@
|
||||
<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>
|
@@ -61,6 +61,7 @@ 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,9 +1,10 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.20"
|
||||
kotlin("jvm") version "2.2.10"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -18,6 +19,7 @@ allprojects {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -7,7 +5,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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -96,12 +96,13 @@ 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.toTypedArray())),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).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)),
|
||||
@@ -131,10 +132,15 @@ 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,6 +13,9 @@ 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;
|
||||
|
||||
|
||||
@@ -26,6 +29,7 @@ enum class BaseDataType {
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
@@ -34,7 +38,8 @@ enum class BaseDataType {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
@@ -50,84 +55,163 @@ 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
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
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) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
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)
|
||||
}
|
||||
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"}
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub
|
||||
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
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)
|
||||
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)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
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 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)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
throw NoSuchElementException("invalid basic 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 =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
@@ -148,6 +232,15 @@ 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()
|
||||
}
|
||||
|
||||
@@ -160,6 +253,30 @@ 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["
|
||||
@@ -187,18 +304,37 @@ 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.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
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)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
|
||||
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
|
||||
@@ -214,22 +350,27 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
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 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 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 && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
@@ -280,10 +421,13 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
else -> throw 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 {
|
||||
|
@@ -72,6 +72,7 @@ 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)
|
||||
@@ -122,6 +123,7 @@ 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
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
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 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.Companion.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
@@ -8,7 +8,7 @@ import java.nio.file.Path
|
||||
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.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 < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
@@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else 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
|
||||
@@ -26,6 +28,8 @@ 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.Companion.FLOAT_MEM_SIZE) {
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
@@ -3,6 +3,7 @@ package prog8.code.target
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
@@ -73,17 +74,12 @@ class VMTarget: ICompilationTarget,
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText(), quiet)
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText(), quiet)
|
||||
else
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -9,12 +7,10 @@ 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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
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,7 +13,6 @@
|
||||
<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,14 +30,17 @@ 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}" // don't prefix autogenerated labels
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // only prefix user defined 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}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +53,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
|
||||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
prefixNamedNode(node)
|
||||
node.parameters.forEach { prefixNamedNode(it) }
|
||||
}
|
||||
is PtSub -> prefixNamedNode(node)
|
||||
is PtFunctionCall -> {
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
@@ -62,11 +62,28 @@ 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
|
||||
@@ -81,8 +98,28 @@ 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) }
|
||||
}
|
||||
|
||||
@@ -139,8 +176,40 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
}
|
||||
}
|
||||
|
||||
return SymbolTableMaker(program, options).make()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun prefixScopedName(name: String, type: Char): String {
|
||||
@@ -175,10 +244,10 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
newValue.add(elt)
|
||||
else {
|
||||
val newAddr = PtAddressOf(elt.position)
|
||||
newAddr.children.add(elt.identifier.prefix(newAddr, st))
|
||||
if(elt.arrayIndexExpr!=null)
|
||||
newAddr.children.add(elt.arrayIndexExpr!!)
|
||||
val newAddr = PtAddressOf(elt.type, false, elt.position)
|
||||
newAddr.add(elt.identifier!!.prefix(newAddr, st))
|
||||
if (elt.arrayIndexExpr != null)
|
||||
newAddr.add(elt.arrayIndexExpr!!)
|
||||
newAddr.parent = arrayValue
|
||||
newValue.add(newAddr)
|
||||
}
|
||||
@@ -207,19 +276,34 @@ private fun PtFunctionCall.withNewName(name: String): PtFunctionCall {
|
||||
}
|
||||
|
||||
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
var target = st.lookup(name)
|
||||
val targetNt: StNodeType
|
||||
val target = st.lookup(name)
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
return this
|
||||
|
||||
if(target==null) {
|
||||
if(name.endsWith("_lsb") || name.endsWith("_msb")) {
|
||||
target = st.lookup(name.dropLast(4))
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
val target2 = st.lookup(name.dropLast(4))
|
||||
if(target2?.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 = when(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) {
|
||||
StNodeType.BLOCK -> 'b'
|
||||
StNodeType.SUBROUTINE, StNodeType.EXTSUB -> 's'
|
||||
StNodeType.LABEL -> 'l'
|
||||
@@ -227,12 +311,10 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -255,10 +337,17 @@ 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 assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val pointerGen = PointerAssignmentsGen(this, allocator)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, pointerGen, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, pointerGen, 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? {
|
||||
|
||||
@@ -316,9 +405,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allVariables.isNotEmpty()) {
|
||||
println("Static variables (not in ZeroPage):")
|
||||
symbolTable.allVariables
|
||||
.filterNot { allocator.isZpVar(it.scopedName) }
|
||||
.sortedBy { it.scopedName }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedName}\t")
|
||||
.filterNot { allocator.isZpVar(it.scopedNameString) }
|
||||
.sortedBy { it.scopedNameString }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedNameString}\t")
|
||||
}
|
||||
}
|
||||
if(allocator.globalFloatConsts.isNotEmpty()) {
|
||||
@@ -330,9 +419,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allMemMappedVariables.isNotEmpty()) {
|
||||
println("Memory mapped:")
|
||||
symbolTable.allMemMappedVariables
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedName} ))
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedNameString} ))
|
||||
.forEach { mvar ->
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedName}")
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedNameString}")
|
||||
}
|
||||
}
|
||||
if(symbolTable.allMemorySlabs.isNotEmpty()) {
|
||||
@@ -420,7 +509,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
|
||||
val name = asmVariableName(st.scopedName)
|
||||
val name = asmVariableName(st.scopedNameString)
|
||||
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)
|
||||
@@ -621,7 +710,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 PtNop, is PtStructDecl, is PtSubSignature -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
}
|
||||
@@ -630,6 +719,8 @@ 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
|
||||
@@ -638,6 +729,9 @@ 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
|
||||
@@ -717,15 +811,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
|
||||
@@ -744,6 +838,7 @@ 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(
|
||||
@@ -796,7 +891,7 @@ class AsmGen6502Internal (
|
||||
when {
|
||||
iterations == 0 -> {}
|
||||
iterations == 1 -> translate(stmt.statements)
|
||||
iterations<0 || iterations>65536 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations !in 0..65536 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations <= 256 -> repeatByteCount(iterations, stmt)
|
||||
else -> repeatWordCount(iterations, stmt)
|
||||
}
|
||||
@@ -838,7 +933,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, isTargetCpu(CpuType.CPU65C02), stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, 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
|
||||
@@ -858,7 +953,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, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
|
||||
out("""
|
||||
cmp #0
|
||||
beq +
|
||||
@@ -1046,17 +1141,18 @@ $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(arrayIdx.variable), true, true, false)
|
||||
return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
|
||||
} else {
|
||||
// print a message when more optimal code is possible for 65C02 cpu
|
||||
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128)
|
||||
val variable = symbolTable.lookup(arrayVariable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128u)
|
||||
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
|
||||
}
|
||||
} else {
|
||||
@@ -1075,18 +1171,20 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
private fun translate(ret: PtReturn) {
|
||||
val returnvalue = ret.children.singleOrNull()
|
||||
val returnvalue = ret.children.singleOrNull() as? PtExpression
|
||||
val sub = ret.definingSub()!!
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
|
||||
if(returnvalue!=null) {
|
||||
if (sub.returns.single().isNumericOrBool) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
||||
val returnDt = sub.signature.returns.single()
|
||||
if (returnDt.isNumericOrBool || returnDt.isPointer) {
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
val addrofValue = PtAddressOf(returnvalue.position)
|
||||
addrofValue.add(returnvalue as PtIdentifier)
|
||||
val addrOfDt = returnvalue.type.typeForAddressOf(false)
|
||||
val addrofValue = PtAddressOf(addrOfDt, false, returnvalue.position)
|
||||
addrofValue.add(returnvalue)
|
||||
addrofValue.parent = ret.parent
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
|
||||
}
|
||||
@@ -1213,11 +1311,11 @@ $repeatLabel""")
|
||||
return null
|
||||
val leftDt = left.type
|
||||
val rightDt = right.type
|
||||
if(leftDt.isUnsignedWord && rightDt.isUnsignedByte)
|
||||
if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedByte)
|
||||
return Pair(left, right)
|
||||
if(leftDt.isUnsignedByte && rightDt.isUnsignedWord)
|
||||
return Pair(right, left)
|
||||
if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) {
|
||||
if((leftDt.isUnsignedWord || leftDt.isPointer) && 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) {
|
||||
@@ -1253,9 +1351,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr) ?: return false
|
||||
if(write) {
|
||||
|
||||
// WRITING TO pointer + offset
|
||||
@@ -1265,8 +1361,10 @@ $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
|
||||
}
|
||||
}
|
||||
@@ -1305,8 +1403,10 @@ $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
|
||||
}
|
||||
}
|
||||
@@ -1343,9 +1443,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
else if(addressExpr.operator=="-") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true) ?: return false
|
||||
if(write) {
|
||||
|
||||
// WRITING TO pointer - offset
|
||||
@@ -1355,8 +1453,10 @@ $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
|
||||
}
|
||||
}
|
||||
@@ -1386,8 +1486,10 @@ $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
|
||||
}
|
||||
}
|
||||
@@ -1418,7 +1520,15 @@ $repeatLabel""")
|
||||
val node = stScope.astNode
|
||||
if(node is PtSubroutineParameter)
|
||||
return node
|
||||
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
val params = node!!.definingSub()?.signature?.children
|
||||
if(params!=null) {
|
||||
for(param in params) {
|
||||
param as PtSubroutineParameter
|
||||
if(param.scopedName==name)
|
||||
return param
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
@@ -1604,7 +1714,9 @@ $repeatLabel""")
|
||||
if(constIndex!=null) {
|
||||
val offset = program.memsizer.memorySize(value.type, constIndex)
|
||||
if(offset<256) {
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable!!)},y")
|
||||
}
|
||||
}
|
||||
cmpViaScratch()
|
||||
|
@@ -408,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.substring(0, 3)
|
||||
val loadinstruction = second.take(3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -46,6 +47,7 @@ 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)
|
||||
@@ -60,12 +62,14 @@ 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)
|
||||
@@ -380,8 +384,9 @@ 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(fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
@@ -390,6 +395,21 @@ 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) {
|
||||
@@ -415,8 +435,11 @@ 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 -> {
|
||||
@@ -438,8 +461,10 @@ 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
|
||||
@@ -465,7 +490,10 @@ 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")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -498,7 +526,9 @@ 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")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
@@ -521,8 +551,10 @@ 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 -> {
|
||||
@@ -545,7 +577,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
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
|
||||
@@ -571,7 +605,9 @@ 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")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -604,7 +640,9 @@ 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")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
@@ -633,7 +671,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable
|
||||
val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
|
||||
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
|
||||
@@ -754,6 +792,58 @@ 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)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" sta ($varname)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta ($varname),y")
|
||||
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)
|
||||
}
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
|
||||
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
@@ -815,7 +905,49 @@ 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(), fcall.position))
|
||||
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)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" lda ($varname)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | lda ($varname),y")
|
||||
} 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1112,7 +1244,9 @@ 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) {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1129,7 +1263,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1211,7 +1347,10 @@ 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
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
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!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1276,7 +1415,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||
return when (value) {
|
||||
is PtIdentifier -> {
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1290,7 +1429,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
@@ -1310,7 +1449,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(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1328,7 +1467,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(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,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 = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
@@ -549,7 +549,7 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -565,7 +565,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16) {
|
||||
if(numElements>=16u) {
|
||||
// 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<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -608,7 +608,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16) {
|
||||
if(numElements>=16u) {
|
||||
// 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<=127) {
|
||||
if(numElements<=127u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #${numElements*2}
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
@@ -653,7 +653,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16) {
|
||||
if(numElements>=16u) {
|
||||
// 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].type.isIntegerOrBool && params[0].register == null
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
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.scopedName)}")
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -139,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(optimizeIntArgsViaCpuRegisters(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 = sub.parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||
val paramValues = 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)
|
||||
@@ -166,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
|
||||
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
when(params.size) {
|
||||
1 -> {
|
||||
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||
@@ -332,14 +333,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==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
if(parameter.value.type.isByte && register.isWord())
|
||||
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(Position.DUMMY)
|
||||
val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
|
@@ -6,11 +6,13 @@ 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) {
|
||||
|
||||
@@ -43,7 +45,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val rightDt = compareCond.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -63,6 +65,16 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val dereference = stmt.condition as? PtPointerDeref
|
||||
if(dereference!=null) {
|
||||
val zpPtrVar = pointergen.deref(dereference)
|
||||
pointergen.loadIndirectByte(zpPtrVar)
|
||||
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}")
|
||||
}
|
||||
|
||||
@@ -885,7 +897,9 @@ _jump jmp (${target.asmLabel})
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
@@ -901,8 +915,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")
|
||||
@@ -980,8 +994,10 @@ _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")
|
||||
@@ -1127,8 +1143,10 @@ _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")
|
||||
@@ -1241,7 +1259,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
|
||||
}
|
||||
@@ -1262,7 +1282,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if (constIndex != null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
|
||||
}
|
||||
@@ -1643,8 +1665,10 @@ _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")
|
||||
@@ -1707,13 +1731,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 {
|
||||
left.identifier.name
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
@@ -1759,13 +1783,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 {
|
||||
left.identifier.name
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
|
@@ -44,6 +44,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
|
||||
structInstances2asm()
|
||||
memorySlabs()
|
||||
footer()
|
||||
}
|
||||
@@ -207,18 +208,20 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
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 slabs_BSS")
|
||||
asmgen.out("\t.bend\n .send BSS_SLABS")
|
||||
}
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
asmgen.out(" .dsection STRUCTINSTANCES\n")
|
||||
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
@@ -274,14 +277,14 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
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 slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
@@ -290,12 +293,12 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,10 +331,9 @@ internal class ProgramAndVarsGen(
|
||||
if (initializers.isNotEmpty()) {
|
||||
asmgen.out("prog8_init_vars\t.block")
|
||||
initializers.forEach { assign ->
|
||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
val constvalue = assign.value as? PtNumber
|
||||
if(constvalue==null || constvalue.number!=0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
asmgen.translate(assign)
|
||||
else
|
||||
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
|
||||
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
@@ -349,7 +351,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -363,11 +365,93 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.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.
|
||||
@@ -417,7 +501,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -435,7 +519,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.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||
|
||||
@@ -491,7 +575,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
@@ -546,12 +630,12 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||
outputStringvar(varname, 0u, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("+")
|
||||
@@ -619,7 +703,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||
asmgen.out(" .section $section")
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0 }
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0u }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
@@ -644,8 +728,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==0 }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
|
||||
notAlignedStrings.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
@@ -692,23 +776,27 @@ 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!!) / 2
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 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!!)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
|
||||
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: Int) {
|
||||
if(align > 1)
|
||||
private fun alignVar(align: UInt) {
|
||||
if(align > 1u)
|
||||
asmgen.out(" .align ${align.toHex()}")
|
||||
}
|
||||
|
||||
@@ -745,7 +833,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)
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -753,7 +841,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
|
||||
alignVar(align)
|
||||
when {
|
||||
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||
@@ -855,7 +943,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||
private fun outputStringvar(varname: String, align: UInt, 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())
|
||||
@@ -920,7 +1008,7 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> 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 == 0 }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
|
||||
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.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
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.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
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.scopedName }
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
|
@@ -20,6 +20,8 @@ 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)
|
||||
@@ -46,6 +48,12 @@ 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,10 +6,11 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
VARIABLE, // non-pointer variable
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
@@ -32,6 +33,7 @@ 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
|
||||
)
|
||||
{
|
||||
@@ -39,13 +41,24 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
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 {
|
||||
@@ -77,6 +90,7 @@ 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")
|
||||
}
|
||||
}
|
||||
@@ -134,11 +148,14 @@ 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 is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("is pointer deref target same as expression? ${this.position}")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> false
|
||||
TargetStorageKind.VOID -> false
|
||||
}
|
||||
@@ -160,8 +177,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
@@ -203,7 +223,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.returns.size>1)
|
||||
if(sub is PtSub && sub.signature.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,6 +9,7 @@ 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?) {
|
||||
@@ -84,22 +85,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isWord -> {
|
||||
target.datatype.isWord || target.datatype.isPointer -> {
|
||||
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, 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.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.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression, block)
|
||||
}
|
||||
else {
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression!!, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +226,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
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
|
||||
if (indexNum!=null) {
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
@@ -301,7 +309,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}")
|
||||
}
|
||||
}
|
||||
@@ -320,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
|
||||
@@ -366,7 +374,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y")
|
||||
}
|
||||
|
||||
target.datatype.isWord -> {
|
||||
@@ -377,11 +385,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 ${target.array.variable.name}_lsb,y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
@@ -450,9 +458,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
|
||||
else
|
||||
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
@@ -504,18 +512,24 @@ 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 = asmgen.asmVariableName(array.variable)
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
TODO("indexed inc/dec on pointer ${array.position}")
|
||||
return false
|
||||
}
|
||||
val arrayvar = asmgen.asmVariableName(arrayVar)
|
||||
when {
|
||||
array.type.isByte -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
|
||||
@@ -884,7 +898,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
|
||||
if (target.datatype == value.type) {
|
||||
if (target.datatype == value.type || (target.datatype.isPointer && value.type.isWord)) {
|
||||
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.
|
||||
@@ -972,7 +986,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)
|
||||
@@ -992,7 +1006,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)
|
||||
@@ -1125,11 +1139,18 @@ $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(value.variable)
|
||||
arrayValue.add(valueVar)
|
||||
arrayValue.add(value.index)
|
||||
binexpr.add(arrayValue)
|
||||
binexpr.parent = value
|
||||
@@ -2256,7 +2277,7 @@ $shortcutLabel:""")
|
||||
|
||||
private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
|
||||
require(dt.isWord)
|
||||
require(valueDt.isInteger)
|
||||
require(valueDt.isInteger || valueDt.isPointer)
|
||||
when {
|
||||
valueDt.isByte -> {
|
||||
// the other variable is a BYTE type so optimize for that
|
||||
@@ -2466,7 +2487,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// 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")
|
||||
@@ -2888,7 +2909,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
@@ -2903,7 +2924,12 @@ $shortcutLabel:""")
|
||||
"-" -> {
|
||||
if(value.index.type.isByte) {
|
||||
// it's an array indexed by a byte so we can use sbc array,y
|
||||
val arrayname = value.variable.name
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
val arrayname = valueVar.name
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords) {
|
||||
asmgen.out("""
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -11,7 +9,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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -11,12 +9,10 @@ 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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
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,9 +13,8 @@
|
||||
<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="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -124,14 +124,83 @@ 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
|
||||
|
||||
val chunks = when (augAssign.operator) {
|
||||
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)
|
||||
@@ -145,9 +214,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
">>=" -> 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
|
||||
}
|
||||
@@ -240,45 +310,47 @@ 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 = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
|
||||
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)
|
||||
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)
|
||||
} 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 = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
||||
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.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.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 = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(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)
|
||||
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)
|
||||
} 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 = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
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.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.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,44 +364,47 @@ 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 = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = arrayVariableName, 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 = array.variable.name)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = arrayVariableName, 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 = array.variable.name)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
}
|
||||
}
|
||||
}
|
||||
"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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,6 +418,7 @@ 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>()
|
||||
@@ -400,80 +476,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
else if(targetArray!=null) {
|
||||
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)
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
return result
|
||||
}
|
||||
else if(targetMemory!=null) {
|
||||
@@ -497,21 +507,21 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
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.
|
||||
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 offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
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)
|
||||
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!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(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)
|
||||
@@ -522,6 +532,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
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)
|
||||
@@ -531,29 +543,188 @@ 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 loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
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
|
||||
|
||||
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 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 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()
|
||||
@@ -565,14 +736,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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
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)
|
||||
}
|
||||
} 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -594,6 +765,7 @@ 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()
|
||||
@@ -605,7 +777,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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -652,20 +824,21 @@ 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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
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)
|
||||
}
|
||||
} 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -695,10 +868,11 @@ 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -804,11 +978,12 @@ 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -861,13 +1036,15 @@ 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 = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = arrayVariableName, 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -927,14 +1104,16 @@ 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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null).also {
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
@@ -944,7 +1123,8 @@ 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)
|
||||
@@ -954,13 +1134,15 @@ 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 = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = arrayVariableName, 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -971,39 +1153,36 @@ 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)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
if (constAddress != null)
|
||||
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress), null)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1016,10 +1195,12 @@ 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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null)
|
||||
return result
|
||||
@@ -1037,24 +1218,26 @@ 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 = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = arrayVariableName, 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1074,13 +1257,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
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 opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||
val ins = if(constAddress!=null)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = constAddress)
|
||||
IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
IRInstruction(opc, vmDt, reg1 = shiftReg, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
}
|
||||
return result
|
||||
@@ -1093,24 +1281,26 @@ 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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = arrayVariableName, 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1129,12 +1319,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
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)
|
||||
}
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, labelSymbol = symbol)
|
||||
,null)
|
||||
}
|
||||
return result
|
||||
@@ -1142,6 +1337,8 @@ 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()
|
||||
@@ -1153,14 +1350,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 = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
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)
|
||||
}
|
||||
} 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 = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
@@ -27,9 +28,12 @@ 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)
|
||||
@@ -46,6 +50,8 @@ 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}")
|
||||
}
|
||||
}
|
||||
@@ -494,8 +500,16 @@ 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 = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||
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)
|
||||
}
|
||||
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -545,7 +559,9 @@ 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) {
|
||||
val variable = arr.variable.name
|
||||
if(arr.variable==null)
|
||||
TODO("support for ptr indexing ${arr.position}")
|
||||
val variable = arr.variable!!.name
|
||||
if(arr.splitWords) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
when(opcodeMemAndReg.first) {
|
||||
@@ -614,7 +630,9 @@ 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()
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(target.variable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -648,6 +666,8 @@ 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) {
|
||||
@@ -656,7 +676,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 = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
@@ -666,7 +686,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 = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -677,7 +697,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 = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
@@ -687,7 +707,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 = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -86,12 +86,107 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
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")
|
||||
@@ -160,19 +255,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
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 resultRegister = codeGen.registers.next(vmDt)
|
||||
val identifier = expr.identifier
|
||||
|
||||
fun loadAddressOfArrayLabel(reg: Int) {
|
||||
if (expr.isMsbForSplitArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
|
||||
} else if (expr.identifier.type.isSplitWordArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
|
||||
} else if (identifier!!.type.isSplitWordArray) {
|
||||
// the _lsb split array comes first in memory
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
|
||||
} else
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
|
||||
}
|
||||
|
||||
if(expr.isFromArrayElement) {
|
||||
@@ -183,30 +277,55 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
|
||||
ixWord
|
||||
} else indexTr.resultReg
|
||||
if(expr.identifier.type.isUnsignedWord) {
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
if(identifier!!.type.isUnsignedWord) {
|
||||
require(!expr.isMsbForSplitArray)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val ptr = codeGen.symbolTable.lookup(expr.identifier.name)
|
||||
val ptr = codeGen.symbolTable.lookup(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 = symbol)
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
|
||||
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 {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
// regular array indexing
|
||||
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
||||
@@ -220,21 +339,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
val ptrWithOffset = mem.address as? PtBinaryExpression
|
||||
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.
|
||||
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 offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
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)
|
||||
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!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(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)
|
||||
@@ -245,6 +364,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
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)
|
||||
@@ -343,11 +463,30 @@ 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 arrayVarSymbol = arrayIx.variable.name
|
||||
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}" }
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
val arrayVarSymbol = arrayVar.name
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
@@ -374,9 +513,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
|
||||
}
|
||||
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||
fun indexByNumber(index: Int) {
|
||||
val memOffset = index * 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)
|
||||
@@ -385,23 +523,61 @@ 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)
|
||||
}
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
}
|
||||
|
||||
fun indexByExpression() {
|
||||
val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
|
||||
result += code
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, 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)
|
||||
@@ -442,9 +618,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.isWord || valueDt.isPointer -> {
|
||||
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)
|
||||
@@ -453,7 +629,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")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.UBYTE -> {
|
||||
@@ -469,7 +645,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")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
@@ -485,7 +661,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")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
@@ -507,7 +683,10 @@ 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)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
BaseDataType.POINTER -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -529,7 +708,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")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
@@ -547,19 +726,30 @@ 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")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird cast 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}")
|
||||
}
|
||||
|
||||
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
|
||||
return when(binExpr.operator) {
|
||||
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)
|
||||
@@ -581,15 +771,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
|
||||
|
||||
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
if(callTarget.scopedNameString 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.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.clear_carry" -> {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(chunk, IRInstruction(Opcode.CLC), null)
|
||||
@@ -638,7 +829,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// return value(s)
|
||||
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||
// TODO: for current implementation 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 {
|
||||
@@ -769,6 +960,9 @@ 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)
|
||||
@@ -778,7 +972,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("invalid node type")
|
||||
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -811,7 +1005,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
when(callTarget.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.push" -> {
|
||||
// push byte
|
||||
val tr = translateExpression(fcall.args.single())
|
||||
@@ -1438,6 +1632,117 @@ 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, UInt>
|
||||
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)
|
||||
var resultFpReg = -1
|
||||
var resultReg = -1
|
||||
if (fieldVmDt == IRDataType.FLOAT)
|
||||
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
else
|
||||
resultReg = codeGen.registers.next(fieldVmDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = field.second.toInt() + extraFieldOffset)
|
||||
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, UInt> {
|
||||
// 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,6 +56,8 @@ 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.
|
||||
@@ -65,17 +67,16 @@ 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.scopedName
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
|
||||
} as PtAssignment?)
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
when(val initValue = initialization?.value){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
|
||||
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())
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
is PtNumber -> {
|
||||
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
require(initValue.number!=0.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.number)
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
@@ -104,6 +105,15 @@ 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) }
|
||||
@@ -257,8 +267,10 @@ 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.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
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()
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@@ -477,7 +489,7 @@ class IRCodeGen(
|
||||
translateForInNonConstantRange(forLoop, loopvar)
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
@@ -501,7 +513,7 @@ class IRCodeGen(
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -557,7 +569,7 @@ class IRCodeGen(
|
||||
val step = iterable.step.number.toInt()
|
||||
if (step==0)
|
||||
throw AssemblyError("step 0")
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -643,7 +655,7 @@ class IRCodeGen(
|
||||
|
||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
|
||||
val loopLabel = createLabelName()
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -1665,7 +1677,7 @@ class IRCodeGen(
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtTypeCast -> {
|
||||
require(cond.type.isBool && cond.value.type.isNumeric)
|
||||
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
|
||||
@@ -1681,6 +1693,9 @@ class IRCodeGen(
|
||||
is PtBinaryExpression -> {
|
||||
translateBinExpr(cond)
|
||||
}
|
||||
is PtPointerDeref -> {
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
@@ -1755,9 +1770,7 @@ class IRCodeGen(
|
||||
private fun isIndirectJump(jump: PtJump): Boolean {
|
||||
if(jump.target.asConstInteger()!=null)
|
||||
return false
|
||||
val identifier = jump.target as? PtIdentifier
|
||||
if(identifier==null)
|
||||
return true
|
||||
val identifier = jump.target as? PtIdentifier ?: return true
|
||||
val symbol = symbolTable.lookup(identifier.name)
|
||||
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
|
||||
}
|
||||
@@ -1848,7 +1861,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, translate(child.parameters), child.returns, child.position)
|
||||
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
@@ -1897,21 +1910,22 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
|
||||
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
|
||||
val result = mutableListOf<IRSubroutine.IRParam>()
|
||||
parameters.forEach {
|
||||
it as PtSubroutineParameter
|
||||
if(it.register==null) {
|
||||
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)
|
||||
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)
|
||||
} else {
|
||||
val reg = it.register
|
||||
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||
@@ -1936,7 +1950,7 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
internal 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))
|
||||
@@ -1947,9 +1961,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
internal 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)
|
||||
@@ -1972,4 +1984,77 @@ class IRCodeGen(
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UInt> {
|
||||
// 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: UInt, 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,6 +53,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
|| simplifyConstantReturns(chunk1, indexedInstructions)
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
@@ -455,4 +456,23 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
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,6 +27,9 @@ 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 ->
|
||||
@@ -47,6 +50,19 @@ 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,6 +14,12 @@ 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 -> { }
|
||||
}
|
||||
}
|
||||
@@ -37,13 +43,18 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
variable.dt,
|
||||
@@ -62,14 +73,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.scopedName))
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
val scopedName = variable.scopedName
|
||||
val scopedName = variable.scopedNameString
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
@@ -94,7 +105,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
|
||||
)
|
||||
} else {
|
||||
val scopedName = try {
|
||||
variable.scopedName
|
||||
variable.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
@@ -109,7 +120,7 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedName
|
||||
constant.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
@@ -122,12 +133,17 @@ private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
else
|
||||
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${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,7 +3,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -8,7 +6,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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
// 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)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
if(addrOf.identifier?.inferType(program)?.isBytes==true)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@@ -332,7 +332,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
@@ -342,6 +343,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("constant fold pointer[i]")
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
@@ -390,9 +394,8 @@ 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.sub == null)
|
||||
val loopvarSimpleDt = loopvar.datatype.base
|
||||
when(loopvarSimpleDt) {
|
||||
require(loopvar.datatype.isBasic)
|
||||
when(val loopvarSimpleDt = loopvar.datatype.base) {
|
||||
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, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = 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) {
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (literal != null && typecast.type.isBasic) {
|
||||
val newLiteral = literal.cast(typecast.type.base, 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) issimpletype typecast.type) {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
@@ -242,14 +242,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
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)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
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)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -262,14 +264,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
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)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
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)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -423,6 +427,29 @@ 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) {
|
||||
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)
|
||||
@@ -499,7 +526,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, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
@@ -516,7 +543,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, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -555,7 +582,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], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -711,9 +738,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, BaseDataType.BYTE, true, expr.position)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
@@ -839,14 +866,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, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.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, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -887,12 +914,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, BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.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), BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -903,12 +930,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, BaseDataType.BYTE, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.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, BaseDataType.BYTE, true, expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.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(program)?.let { target ->
|
||||
identifier.targetStatement()?.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(program) ?: return emptyList()
|
||||
val target = it.targetStatement() ?: 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) as? Subroutine
|
||||
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) 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) as? Subroutine
|
||||
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) 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}"
|
||||
@@ -249,7 +249,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
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
|
||||
return existing !is VarDecl && existing !is StructFieldRef
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@@ -19,7 +19,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in functions.purefunctionNames) {
|
||||
if("ignore_unused" !in parent.definingBlock.options())
|
||||
errors.info("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,14 @@ 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) arg.identifier else null
|
||||
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
|
||||
}
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
@@ -146,7 +153,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, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@@ -161,7 +168,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, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@@ -174,7 +181,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, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, position = 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))
|
||||
@@ -341,6 +348,39 @@ 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,6 +181,11 @@ 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
|
||||
@@ -191,6 +196,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.info("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||
}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
@@ -18,18 +16,15 @@ 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.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
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 {
|
||||
@@ -83,6 +78,8 @@ gversion {
|
||||
classPackage = "prog8.buildversion"
|
||||
className = "Version"
|
||||
language = "kotlin"
|
||||
debug = false
|
||||
annotate = true
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
|
@@ -13,9 +13,7 @@
|
||||
<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" />
|
||||
@@ -25,6 +23,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -440,6 +440,7 @@ 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
|
||||
@@ -679,7 +680,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -16,26 +16,6 @@ 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,6 +149,29 @@ 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
|
||||
|
@@ -376,7 +376,7 @@ hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %1111
|
||||
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
|
||||
; note: this can be even faster if we also have a 320 word x-lookup table, but hey, that's a lot of memory.
|
||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; see https://codebase64.net/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
|
@@ -57,7 +57,7 @@ cbm {
|
||||
|
||||
; ---- CBM ROM kernal routines (C64 addresses) ----
|
||||
|
||||
extsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
extsub $AB1E = STROUT(str 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,6 +445,7 @@ 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
|
||||
@@ -686,7 +687,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -15,25 +15,6 @@ 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
|
||||
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
^^ubyte @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
|
||||
uword error_message ; pointer to error message, or 0 if all ok
|
||||
^^ubyte 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() -> uword {
|
||||
sub diskname() -> str {
|
||||
; -- 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
|
||||
uword list_pattern
|
||||
^^ubyte 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(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(str 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(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list(str 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(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_dirs(str 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(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_files(str 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
|
||||
|
||||
uword nameptr = &list_filename
|
||||
^^ubyte 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(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
asmsub f_readline(^^ubyte 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() -> uword {
|
||||
sub status() -> str {
|
||||
; -- 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
|
||||
; }
|
||||
|
||||
uword messageptr = &list_filename
|
||||
^^ubyte 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(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save(str 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(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save_raw(str filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
sub internal_save_routine(str 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(uword filenameptr, uword address_override) -> uword {
|
||||
sub load(str 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(uword filenameptr, uword startaddress) -> uword {
|
||||
sub load_raw(str 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(uword libnameptr, uword libaddress) -> uword {
|
||||
sub loadlib(str libnameptr, uword libaddress) -> uword {
|
||||
return internal_load_routine(libnameptr, libaddress, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
sub internal_load_routine(str 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(uword filenameptr) {
|
||||
sub delete(str 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(uword oldfileptr, uword newfileptr) {
|
||||
sub rename(str oldfileptr, str 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(uword commandptr) {
|
||||
sub send_command(str 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() -> uword {
|
||||
sub curdir() -> str {
|
||||
; 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
|
||||
|
@@ -47,6 +47,12 @@ emudbg {
|
||||
}
|
||||
}
|
||||
|
||||
sub console_nl() {
|
||||
; write a newline to the debug output console.
|
||||
; because '\n' gets encoded in the x16 value for it (13) which is different than what the shell expects (10).
|
||||
console_chrout(10)
|
||||
}
|
||||
|
||||
sub console_chrout(ubyte char) {
|
||||
; note: make sure the character is in Iso encoding.
|
||||
if is_emulator()
|
||||
|
@@ -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) -> 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.
|
||||
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.
|
||||
|
||||
; 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, uword sctextptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str 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, uword textptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str 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)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr+num_sprites)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
tovera(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 = @(ypositions_ptr)
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr+num_sprites)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA0 = @(cx16.r0)
|
||||
cx16.VERA_DATA1 = @(cx16.r0+num_sprites)
|
||||
cx16.r0 ++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,20 +109,19 @@ 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)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
tovera(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 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA0 = @(cx16.r0)
|
||||
cx16.r0 ++
|
||||
cx16.VERA_DATA1 = @(cx16.r0)
|
||||
cx16.r0 ++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1470,7 +1470,7 @@ sub search_x16edit() -> ubyte {
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(uword args_ptr, ubyte args_size) {
|
||||
sub set_program_args(str 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(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
|
||||
asmsub get_program_args(^^ubyte 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,6 +1560,7 @@ 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
|
||||
@@ -1758,7 +1759,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -17,26 +17,6 @@ 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,6 +119,7 @@ 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
|
||||
@@ -136,6 +137,7 @@ 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) {
|
||||
uword strptr
|
||||
^^ubyte strptr
|
||||
for strptr in logo_lines {
|
||||
txt.plot(column, row)
|
||||
txt.print(strptr)
|
||||
@@ -15,7 +15,7 @@ cx16logo {
|
||||
}
|
||||
|
||||
sub logo() {
|
||||
uword strptr
|
||||
^^ubyte strptr
|
||||
for strptr in logo_lines {
|
||||
txt.print(strptr)
|
||||
txt.nl()
|
||||
|
@@ -3,7 +3,7 @@
|
||||
;
|
||||
; some more interesting routines can be found here:
|
||||
; http://6502org.wikidot.com/software-math
|
||||
; http://codebase64.org/doku.php?id=base:6502_6510_maths
|
||||
; https://codebase64.net/doku.php?id=base:6502_6510_maths
|
||||
; https://github.com/TobyLobster/multiply_test
|
||||
; https://github.com/TobyLobster/sqrt_test
|
||||
|
||||
@@ -353,7 +353,7 @@ _divisor .word ?
|
||||
randword .proc
|
||||
; -- 16 bit pseudo random number generator into AY
|
||||
; default seed = $00c2 $1137. NOTE: uses self-modifying code so won't work in ROM (use randword_rom instead)
|
||||
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||
; routine from https://codebase64.net/doku.php?id=6502_6510_maths:x_abc_random_number_generator_8_16_bit
|
||||
inc x1
|
||||
clc
|
||||
x1=*+1
|
||||
@@ -377,7 +377,7 @@ b1=*+1
|
||||
randword_rom .proc
|
||||
; -- 16 bit pseudo random number generator into AY. Can run from ROM.
|
||||
; NOTE: you have to set the initial seed using randseed_rom! (a good default seed = $00c2 $1137)
|
||||
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||
; routine from https://codebase64.net/doku.php?id=6502_6510_maths:x_abc_random_number_generator_8_16_bit
|
||||
inc _x1
|
||||
clc
|
||||
lda _x1
|
||||
|
@@ -380,7 +380,7 @@ _quadrant_region_to_direction:
|
||||
asmsub atan2(ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A {
|
||||
;; Calculate the angle, in a 256-degree circle, between two points into A.
|
||||
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
|
||||
;; https://www.codebase64.org/doku.php?id=base:8bit_atan2_8-bit_angle
|
||||
;; http://codebase64.net/doku.php?id=base:8bit_atan2_8-bit_angle
|
||||
;; This uses 2 large lookup tables so uses a lot of memory but is super fast.
|
||||
|
||||
%asm {{
|
||||
@@ -547,7 +547,7 @@ log2_tab
|
||||
}}
|
||||
}
|
||||
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
sub crc16(^^ubyte 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(uword data, uword length) {
|
||||
sub crc32(^^ubyte 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,6 +104,7 @@ 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
|
||||
@@ -162,7 +163,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@@ -15,26 +15,6 @@ 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)
|
||||
}
|
||||
@@ -225,20 +205,16 @@ sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
jsr home
|
||||
cpy #0
|
||||
beq +
|
||||
- lda #29
|
||||
jsr chrout
|
||||
dey
|
||||
bne -
|
||||
+ cpx #0
|
||||
beq +
|
||||
- lda #17
|
||||
jsr chrout
|
||||
dex
|
||||
bne -
|
||||
+ rts
|
||||
stx $d8
|
||||
txa
|
||||
asl a
|
||||
tax
|
||||
lda setchr._screenrows,x
|
||||
sta $c4
|
||||
lda setchr._screenrows+1,x
|
||||
sta $c5
|
||||
sty $c6
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
@@ -401,8 +401,17 @@ _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
|
||||
; -- read the word value on the address in AY, into AY
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
|
@@ -101,7 +101,7 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
sub diskname() -> uword {
|
||||
sub diskname() -> str {
|
||||
; -- 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
|
||||
uword list_pattern
|
||||
^^ubyte 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(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(str 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.
|
||||
@@ -187,7 +187,7 @@ io_error:
|
||||
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list(str 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(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_dirs(str 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(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_files(str 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
|
||||
|
||||
uword nameptr = &list_filename
|
||||
^^ubyte 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(uword filenameptr) -> bool {
|
||||
sub f_open(str 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(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.
|
||||
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.
|
||||
; 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(uword filenameptr) -> bool {
|
||||
sub f_open_w(str 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() -> uword {
|
||||
sub status() -> str {
|
||||
; -- 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
|
||||
; }
|
||||
|
||||
uword messageptr = &list_filename
|
||||
^^ubyte 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(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
sub save(str 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(uword filenameptr, uword address_override) -> uword {
|
||||
sub load(str 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(uword filenameptr, uword start_address) -> uword {
|
||||
sub load_raw(str 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(uword libnameptr, uword libaddress) -> uword {
|
||||
sub loadlib(str libnameptr, uword libaddress) -> uword {
|
||||
return load(libnameptr, libaddress)
|
||||
}
|
||||
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
sub delete(str 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(uword oldfileptr, uword newfileptr) {
|
||||
sub rename(str oldfileptr, str 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(uword commandptr) {
|
||||
sub send_command(str commandptr) {
|
||||
; -- send a dos command to the drive
|
||||
cbm.SETNAM(strings.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
|
@@ -3,6 +3,35 @@ 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
|
||||
@@ -136,7 +165,7 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
asmsub input_chars (^^ubyte 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 ubyte
|
||||
return cx16.r3-(haystack as uword) 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(uword bytearray @AY, ubyte num_elements @X) {
|
||||
asmsub gnomesort_ub(^^ubyte bytearray @AY, ubyte num_elements @X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -43,21 +43,21 @@ _done
|
||||
}}
|
||||
}
|
||||
|
||||
sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) {
|
||||
sub gnomesort_by_ub(^^ubyte @requirezp ub_keys, ^^uword wordvalues, 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 uw_keys[pos]>=uw_keys[pos-1]
|
||||
if ub_keys[pos]>=ub_keys[pos-1]
|
||||
pos++
|
||||
else {
|
||||
; swap elements
|
||||
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.r0L = ub_keys[pos-1]
|
||||
ub_keys[pos-1] = ub_keys[pos]
|
||||
ub_keys[pos] = cx16.r0L
|
||||
^^uword @requirezp vptr = wordvalues + (pos-1)
|
||||
cx16.r0 = peekw(vptr)
|
||||
pokew(vptr, peekw(vptr+2))
|
||||
pokew(vptr+2, cx16.r0)
|
||||
pokew(vptr, peekw(vptr+1))
|
||||
pokew(vptr+1, cx16.r0)
|
||||
|
||||
pos--
|
||||
if_z
|
||||
@@ -66,20 +66,20 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r1L = pos-2
|
||||
if peekw(values+pos) >= peekw(values + cx16.r1L)
|
||||
if peekw(wordvalues+pos) >= peekw(wordvalues + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0 = peekw(values + cx16.r1L)
|
||||
pokew(values + cx16.r1L, peekw(values + pos))
|
||||
pokew(values + pos, cx16.r0)
|
||||
cx16.r0 = peekw(wordvalues + cx16.r1L)
|
||||
pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
|
||||
pokew(wordvalues + 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. Clobbers R0 and R1.
|
||||
; Max number of elements is 128 to keep indexing code size small and fast. 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(uword @requirezp values, ubyte num_elements) {
|
||||
sub shellsort_ub(^^ubyte @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (unsigned bytes).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
@@ -138,6 +138,7 @@ _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--
|
||||
@@ -160,7 +161,8 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; TODO convert to ^^uword once code size regression is fixed
|
||||
sub shellsort_by_ub(^^ubyte @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
|
||||
@@ -186,6 +188,7 @@ _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.
|
||||
@@ -236,7 +239,7 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
asmsub string_comparator(uword string1 @R0, uword string2 @R1) -> bool @Pc {
|
||||
asmsub string_comparator(str string1 @R0, str 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(uword string @AY) clobbers(A) -> ubyte @Y {
|
||||
asmsub length(str 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(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) {
|
||||
asmsub left(str source @AX, ubyte length @Y, str 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(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
||||
asmsub right(str source @AY, ubyte length @X, str 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(uword source @R0, ubyte start @A, ubyte length @Y, uword target @R1) clobbers(A, Y) {
|
||||
asmsub slice(str source @R0, ubyte start @A, ubyte length @Y, str 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(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
asmsub find(str 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(uword string @AY) -> ubyte @A, bool @Pc {
|
||||
asmsub find_eol(str 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(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
asmsub rfind(str 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(uword string @AY, ubyte character @X) -> bool @Pc {
|
||||
asmsub contains(str 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(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y {
|
||||
asmsub copy(str source @R0, str 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(uword target @R0, uword suffix @R1) clobbers(Y) -> ubyte @A {
|
||||
asmsub append(str target @R0, str 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(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
||||
asmsub compare(str string1 @R0, str 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(uword string1 @R0, uword string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A {
|
||||
asmsub ncompare(str string1 @R0, str 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(uword st @AY) -> ubyte @Y {
|
||||
asmsub lower(str 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(uword st @AY) -> ubyte @Y {
|
||||
asmsub upper(str 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(uword @zp compressed, uword @zp target, uword maxsize) -> uword {
|
||||
sub decode_rle(^^ubyte @zp compressed, ^^ubyte @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(uword data, uword size, uword target, bool is_last_block) -> uword {
|
||||
sub encode_rle(^^ubyte data, uword size, ^^ubyte 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
|
||||
uword orig_target = target
|
||||
^^ubyte 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++
|
||||
uword dataptr = data + literals_start_idx
|
||||
^^ubyte 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
|
||||
uword out_ptr = &string_out
|
||||
^^ubyte 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, uword out_ptr) {
|
||||
sub internal_str_ub(ubyte value, str 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
|
||||
uword out_ptr = &string_out
|
||||
^^ubyte 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
|
||||
uword out_ptr = &string_out
|
||||
^^ubyte 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
|
||||
uword out_ptr = &string_out
|
||||
^^ubyte 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, uword out_ptr) {
|
||||
sub internal_str_uw(uword value, str 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(uword filenameptr) -> bool {
|
||||
sub f_open(str 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(uword bufferpointer, uword num_bytes) -> uword {
|
||||
sub f_read(str 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(uword bufferpointer) -> uword {
|
||||
sub f_read_all(str 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(uword bufptr) -> ubyte {
|
||||
sub f_readline(str 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(uword filenameptr) -> bool {
|
||||
sub f_open_w(str 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,13 +133,13 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||
sub f_write(str 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 {
|
||||
%ir {{
|
||||
loadm.w r0,diskio.f_write.bufferpointer
|
||||
loadi.b r99100,r0
|
||||
loadm.w r99000,diskio.f_write.bufferpointer
|
||||
loadi.b r99100,r99000
|
||||
syscall 55 (r99100.b): r99100.b
|
||||
storem.b r99100,$ff02
|
||||
}}
|
||||
@@ -185,7 +185,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub curdir() -> uword {
|
||||
sub curdir() -> str {
|
||||
; return current directory name or 0 if error
|
||||
%ir {{
|
||||
syscall 48 (): r99000.w
|
||||
@@ -204,7 +204,7 @@ diskio {
|
||||
}
|
||||
|
||||
|
||||
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
|
||||
%ir {{
|
||||
load.b r99100,0
|
||||
loadm.w r99000,diskio.save.filenameptr
|
||||
@@ -216,12 +216,12 @@ diskio {
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
|
||||
%ir {{
|
||||
load.b r99100,1
|
||||
loadm.w r99000,diskio.save.filenameptr
|
||||
loadm.w r99001,diskio.save.start_address
|
||||
loadm.w r99002,diskio.save.savesize
|
||||
loadm.w r99000,diskio.save_raw.filenameptr
|
||||
loadm.w r99001,diskio.save_raw.start_address
|
||||
loadm.w r99002,diskio.save_raw.savesize
|
||||
syscall 42 (r99100.b, r99000.w, r99001.w, r99002.w): r99100.b
|
||||
returnr.b r99100
|
||||
}}
|
||||
@@ -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(uword filenameptr, uword address_override) -> uword {
|
||||
sub load(str 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(uword filenameptr, uword start_address) -> uword {
|
||||
sub load_raw(str 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(uword filenameptr) {
|
||||
sub delete(str filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.delete.filenameptr
|
||||
@@ -262,7 +262,7 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
sub rename(str oldfileptr, str newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
%ir {{
|
||||
loadm.w r99000,diskio.rename.oldfileptr
|
||||
|
@@ -316,7 +316,7 @@ math {
|
||||
return w2-w1
|
||||
}
|
||||
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
sub crc16(^^ubyte 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(uword data, uword length) {
|
||||
sub crc32(^^ubyte 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(uword @requirezp values, ubyte num_elements) {
|
||||
sub shellsort_ub(^^ubyte @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 = peekw(values+i*$0002)
|
||||
uword @zp temp = values[i]
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(values+k*2)
|
||||
uword @zp v = values[k]
|
||||
if v <= temp break
|
||||
pokew(values+j*2, v)
|
||||
values[j] = v
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(values+j*2, temp)
|
||||
values[j] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
sub shellsort_by_ub(^^ubyte @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 = peekw(wordvalues + i*$0002)
|
||||
uword temp_wv = wordvalues[i]
|
||||
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
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
wordvalues[j] = wordvalues[k]
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
wordvalues[j] = 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 = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
uword @zp temp = uw_keys[i]
|
||||
uword temp_wv = wordvalues[i]
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
uword @zp v = uw_keys[k]
|
||||
if v <= temp break
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
uw_keys[j] = v
|
||||
wordvalues[j] = wordvalues[k]
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
uw_keys[j] = temp
|
||||
wordvalues[j] = temp_wv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -82,7 +82,7 @@ strings {
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte, bool {
|
||||
sub rfind(str 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,6 +13,7 @@ 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
|
||||
@@ -47,7 +48,7 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_stringcopy(uword source, uword tgt) {
|
||||
sub internal_stringcopy(str source, str tgt) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%ir {{
|
||||
loadm.w r99000,sys.internal_stringcopy.source
|
||||
|
@@ -53,6 +53,21 @@ 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
|
||||
@@ -136,7 +151,7 @@ sub print_w (word value) {
|
||||
print(conv.str_w(value))
|
||||
}
|
||||
|
||||
sub input_chars (uword buffer) -> ubyte {
|
||||
sub input_chars (str 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)
|
||||
throw SyntaxError("sizeof argument should be an identifier or number", 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")
|
||||
|
||||
val dt = args[0].inferType(program)
|
||||
if(dt.isKnown) {
|
||||
if(args[0] is NumericLiteral)
|
||||
if(args[0] is NumericLiteral || args[0] is AddressOf)
|
||||
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
val target = (args[0] as? IdentifierReference)?.targetStatement()
|
||||
?: throw SyntaxError("wrong argument type", position)
|
||||
|
||||
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(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
}
|
||||
} else {
|
||||
val identifier = args[0] as? IdentifierReference
|
||||
@@ -128,11 +128,17 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
}
|
||||
}
|
||||
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
@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, args.errors)
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, 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,7 +456,6 @@ 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)
|
||||
@@ -468,7 +467,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
||||
errors.report()
|
||||
program.constantFold(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.reorderStatements(errors)
|
||||
program.reorderStatements(compilerOptions, errors)
|
||||
errors.report()
|
||||
program.desugaring(errors, compilerOptions)
|
||||
errors.report()
|
||||
|
@@ -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") }
|
||||
.filter { it.severity == MessageSeverity.ERROR && (it.message.contains("undefined symbol") || it.message.contains("no such field")) }
|
||||
.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
@@ -20,8 +20,8 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
|
||||
checker.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||
val reorder = StatementReorderer(this, errors)
|
||||
internal fun Program.reorderStatements(options: CompilationOptions, errors: IErrorReporter) {
|
||||
val reorder = StatementReorderer(this, options, errors)
|
||||
reorder.visit(this)
|
||||
if(errors.noErrors()) {
|
||||
reorder.applyModifications()
|
||||
@@ -119,7 +119,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val lit2decl = LiteralsToAutoVars(this, errors)
|
||||
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(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 = this.targetStatement(program)) {
|
||||
when (val targetStatement = targetStatement(program.builtinFunctions)) {
|
||||
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 Alias, is StructDecl, is StructFieldRef -> {
|
||||
return targetStatement
|
||||
}
|
||||
null -> {
|
||||
|
@@ -40,7 +40,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
override fun visit(alias: Alias) {
|
||||
if(alias.target.targetStatement(program)==null)
|
||||
if(alias.target.targetStatement(program.builtinFunctions)==null)
|
||||
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
super.visit(decl)
|
||||
}
|
||||
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
@@ -169,13 +168,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
|
||||
private fun visitFunctionCall(call: IFunctionCall) {
|
||||
if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) {
|
||||
val target = call.target.targetStatement(program)
|
||||
val target = call.target.targetStatement(program.builtinFunctions)
|
||||
if(target==null) {
|
||||
errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position)
|
||||
return
|
||||
}
|
||||
}
|
||||
when (val target = call.target.targetStatement(program)) {
|
||||
when (val target = call.target.targetStatement(program.builtinFunctions)) {
|
||||
is Subroutine -> {
|
||||
val expectedNumberOfArgs: Int = target.parameters.size
|
||||
if(call.args.size != expectedNumberOfArgs) {
|
||||
@@ -215,7 +214,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord)
|
||||
errors.err("wrong address variable datatype, expected uword", call.target.position)
|
||||
}
|
||||
is Alias -> {}
|
||||
is Alias, is StructDecl, is StructFieldRef -> {}
|
||||
null -> {}
|
||||
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
|
||||
}
|
||||
|
@@ -124,7 +124,14 @@ class AstPreprocessor(val program: Program,
|
||||
// we need to handle multi-decl here too, the desugarer maybe has not processed it here yet...
|
||||
if(decl.value!=null) {
|
||||
decl.names.forEach { name ->
|
||||
val target = AssignTarget(IdentifierReference(listOf(name), decl.position), null, null, null, false, decl.position)
|
||||
val target = AssignTarget(
|
||||
IdentifierReference(listOf(name), decl.position),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
)
|
||||
val assign = Assignment(target.copy(), decl.value!!.copy(), AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.InsertAfter(decl, assign, scope))
|
||||
}
|
||||
@@ -140,7 +147,14 @@ class AstPreprocessor(val program: Program,
|
||||
} else {
|
||||
// handle declaration of a single variable
|
||||
if(decl.value!=null && !decl.datatype.isIterable) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
|
||||
val target = AssignTarget(
|
||||
IdentifierReference(listOf(decl.name), decl.position),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
decl.value = null
|
||||
@@ -186,6 +200,19 @@ class AstPreprocessor(val program: Program,
|
||||
return makeUnSplitArray(decl)
|
||||
}
|
||||
|
||||
// convert all antlr names to structs
|
||||
val antlrTypeName = decl.datatype.subTypeFromAntlr
|
||||
if(antlrTypeName!=null) {
|
||||
val node = decl.definingScope.lookup(antlrTypeName)
|
||||
if(node==null) {
|
||||
errors.err("cannot find struct type ${antlrTypeName.joinToString(".")}", decl.position)
|
||||
} else if(node is StructDecl) {
|
||||
decl.datatype.setActualSubType(node)
|
||||
} else if(antlrTypeName.size==1 && antlrTypeName[0] in program.builtinFunctions.names) {
|
||||
errors.err("builtin function can only be called, not used as a type name", decl.position)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -236,10 +263,26 @@ class AstPreprocessor(val program: Program,
|
||||
errors.err("can only use R0-R15 as register param for normal subroutines", it.position)
|
||||
}
|
||||
}
|
||||
if(mods.isNotEmpty())
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
subroutine.returntypes.forEach {
|
||||
if(it.subTypeFromAntlr!=null) {
|
||||
val struct = subroutine.lookup(it.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
subroutine.parameters.forEach {
|
||||
if(it.type.subTypeFromAntlr!=null) {
|
||||
val struct = subroutine.lookup(it.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.type.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -248,6 +291,19 @@ class AstPreprocessor(val program: Program,
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
|
||||
checkStringParam(functionCallExpr as IFunctionCall, stmtOfExpression)
|
||||
|
||||
if(functionCallExpr.target.nameInSource==listOf("sizeof")) {
|
||||
val arg = functionCallExpr.args.firstOrNull()
|
||||
if (arg is PtrDereference) {
|
||||
// replace sizeof(ptr^^) with sizeof(type that ptr points to)
|
||||
val dt = arg.inferType(program)
|
||||
if(dt.isKnown) {
|
||||
val dtName = dt.getOrUndef().toString()
|
||||
val newArg = IdentifierReference(dtName.split("."), arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(arg, newArg, functionCallExpr))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -257,13 +313,57 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
val tgt = alias.target.targetStatement(program)
|
||||
val tgt = alias.target.targetStatement(program.builtinFunctions)
|
||||
if(tgt is Block) {
|
||||
errors.err("cannot alias blocks", alias.target.position)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// convert all antlr names to structs
|
||||
if(typecast.type.subTypeFromAntlr!=null) {
|
||||
val struct = typecast.definingScope.lookup(typecast.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
typecast.type.setActualSubType(struct)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(field: StructFieldRef, parent: Node): Iterable<IAstModification> {
|
||||
if(field.type.subTypeFromAntlr!=null) {
|
||||
val struct = field.definingScope.lookup(field.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
field.type.setActualSubType(struct)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> {
|
||||
// convert all antlr names to structs
|
||||
struct.fields.forEach {
|
||||
if(it.first.subTypeFromAntlr!=null) {
|
||||
val struct = struct.definingScope.lookup(it.first.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.first.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
|
||||
// convert str fields to ^^ubyte
|
||||
val convertedFields = struct.fields.map {
|
||||
if(it.first.isString)
|
||||
DataType.pointer(BaseDataType.UBYTE) to it.second // replace str field with ^^ubyte field
|
||||
else
|
||||
it.first to it.second
|
||||
}.toTypedArray()
|
||||
|
||||
if(!convertedFields.contentEquals(struct.fields))
|
||||
convertedFields.copyInto(struct.fields)
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
|
||||
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
|
||||
if(targetStatement!=null) {
|
||||
|
@@ -88,7 +88,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
) {
|
||||
if(!subroutine.inline) {
|
||||
if(outerScope is Subroutine && outerScope.returntypes.isNotEmpty()) {
|
||||
if(outerScope.returntypes.size>1 || !outerScope.returntypes[0].isNumericOrBool) {
|
||||
if(outerScope.returntypes.size>1 || !(outerScope.returntypes[0].isNumericOrBool || outerScope.returntypes[0].isPointer)) {
|
||||
errors.err("subroutine is missing a return statement to avoid falling through into nested subroutine", outerStatements[subroutineStmtIdx-1].position)
|
||||
} else {
|
||||
val zero = defaultZero(outerScope.returntypes[0].base, Position.DUMMY)
|
||||
|
@@ -9,7 +9,6 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.isByte
|
||||
import prog8.code.core.isWord
|
||||
|
||||
|
||||
@@ -29,44 +28,36 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(typecast.type==sourceDt.base)
|
||||
if(typecast.type==sourceDt)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(sourceDt.isPassByRef) {
|
||||
if(typecast.type == BaseDataType.UWORD) {
|
||||
if(typecast.type.isUnsignedWord) {
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter()) {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
typecast.expression,
|
||||
parent
|
||||
)
|
||||
)
|
||||
if (identifier != null) {
|
||||
return if (identifier.isSubroutineParameter()) {
|
||||
listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
} else {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
AddressOf(identifier, null, false, typecast.position),
|
||||
parent
|
||||
)
|
||||
)
|
||||
listOf(IAstModification.ReplaceNode(typecast,
|
||||
AddressOf(identifier, null, null, false, false,typecast.position), parent))
|
||||
}
|
||||
} else if(typecast.expression is IFunctionCall) {
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
typecast.expression,
|
||||
parent
|
||||
)
|
||||
)
|
||||
} else if (typecast.expression is IFunctionCall) {
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
} else if(sourceDt.isString && typecast.type.isPointer && typecast.type.sub==BaseDataType.UBYTE) {
|
||||
// casting a string to a ^^ubyte is just taking the address of the string.
|
||||
val addr = AddressOf(typecast.expression as IdentifierReference, null, null, false, true, typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, addr, parent))
|
||||
} else {
|
||||
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
|
||||
}
|
||||
}
|
||||
|
||||
if(typecast.type.isUnsignedWord && sourceDt.isPointer) {
|
||||
// remove all typecasts of pointers to unsigned words.
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user