mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 16:23:42 +00:00
Compare commits
131 Commits
Author | SHA1 | Date | |
---|---|---|---|
b8178c6c8d | |||
4a0f15eb88 | |||
c4f53fe525 | |||
8c93ec52de | |||
befe0fff2a | |||
b6a837cbea | |||
4861973899 | |||
c593e4b500 | |||
5bf78c20d4 | |||
5c672130e6 | |||
d8214d4f12 | |||
64d1f09ce0 | |||
47d0f0ea40 | |||
2d85fd093e | |||
d936568b76 | |||
4598a83e8e | |||
f4bf00ad31 | |||
07fde7f6cc | |||
729209574e | |||
f28206d989 | |||
0c81b32cac | |||
11216017cb | |||
a7b9f53967 | |||
1fa2e2e37d | |||
f67d5faeb7 | |||
5cbf859458 | |||
629ed74d09 | |||
ca2af2ca63 | |||
52ab089615 | |||
01461a196d | |||
04832f052a | |||
c8b2c8ae50 | |||
1b81c7fb22 | |||
9ccda0247e | |||
a7df4dcf25 | |||
d91f47c791 | |||
a9ac4e7f44 | |||
fc3ec57437 | |||
266f6ab919 | |||
6218c1c00b | |||
cc81d6fe82 | |||
69f9102f2d | |||
beb9275982 | |||
abe48713f2 | |||
82cfaf2fbb | |||
3d3bc4738f | |||
2d0746f5a4 | |||
9c71e2f1c8 | |||
134fd62da8 | |||
2afd283582 | |||
c66734bab0 | |||
8e56a61f95 | |||
d265271148 | |||
b40e397b28 | |||
35ff1d996a | |||
deea0b05cb | |||
c50c9ca545 | |||
a819b4a5a5 | |||
df2d7d4734 | |||
79ce4098cf | |||
374464a1f8 | |||
c8d0bf27af | |||
6e4ae034b2 | |||
52b560e72d | |||
9b971ad222 | |||
3613162d09 | |||
3a272e998d | |||
d4c750beb4 | |||
84b31e65e1 | |||
7b802bfd3d | |||
f9c4632b8d | |||
e4764cd8a6 | |||
dd78a3a686 | |||
94c06e13f4 | |||
e8bebe5a75 | |||
5b0e1b4f9e | |||
8c0a93779b | |||
9241479da4 | |||
8ffca93cd5 | |||
7fea0c124a | |||
20dbdb20d2 | |||
e6b8e2e8be | |||
7c5b7f77cc | |||
de84547a21 | |||
44676756ae | |||
b399b0f182 | |||
1152191f48 | |||
af1b07ad44 | |||
b8113fff1e | |||
ff6948cf2d | |||
fd25e85d59 | |||
c07cd72e85 | |||
e2c101206c | |||
92276b5769 | |||
a2133f61a8 | |||
199adbbcf0 | |||
dc316fd7b4 | |||
025183602f | |||
db4619a9d9 | |||
451e527b7c | |||
54dd3a00df | |||
03c5dab79d | |||
1fdee861e8 | |||
c12bf991b3 | |||
78a097585d | |||
39132327cc | |||
dc32318cec | |||
592f74124c | |||
e5e63cc5ac | |||
f40e0f786d | |||
ebd9f1471b | |||
d76547ead4 | |||
4600772e05 | |||
ed597423cd | |||
f20ca06f85 | |||
a636d3f394 | |||
043df18daa | |||
96996bf18e | |||
f350137a14 | |||
b7a6f3ec75 | |||
6c34672549 | |||
e779a07bce | |||
9a36e8ba3b | |||
c968bacb01 | |||
25199dfb43 | |||
48fed4e6fb | |||
fc253237c9 | |||
589948c7f4 | |||
7e69690605 | |||
95f498ba9b | |||
fd07ae5225 |
9
.idea/kotlinc.xml
generated
Normal file
9
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
|
<option name="jvmTarget" value="11" />
|
||||||
|
</component>
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.8.20" />
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.idea/libraries/antlr_antlr4.xml
generated
7
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,16 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="antlr.antlr4" type="repository">
|
<library name="antlr.antlr4" type="repository">
|
||||||
<properties maven-id="org.antlr:antlr4:4.11.1">
|
<properties maven-id="org.antlr:antlr4:4.12.0">
|
||||||
<exclude>
|
<exclude>
|
||||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||||
</exclude>
|
</exclude>
|
||||||
</properties>
|
</properties>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.12.0/antlr4-4.12.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.11.1/antlr4-runtime-4.11.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.12.0/antlr4-runtime-4.12.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
30
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
30
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
@ -1,23 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
|
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
18
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
18
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
@ -1,21 +1,21 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.5.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.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.2.0/opentest4j-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
22
.idea/libraries/io_kotest_property_jvm.xml
generated
22
.idea/libraries/io_kotest_property_jvm.xml
generated
@ -1,22 +1,22 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.property.jvm" type="repository">
|
<library name="io.kotest.property.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-property-jvm:5.5.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.3.2/kotest-property-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.5.5/kotest-property-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
64
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
64
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
@ -1,46 +1,50 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.5.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.5.5/kotest-runner-junit5-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.5.5/kotest-framework-api-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.4/kotlinx-coroutines-test-jvm-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.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$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.3.2/kotest-framework-engine-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.5.5/kotest-framework-engine-jvm-5.5.5.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.154/classgraph-4.8.154.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.4/kotlinx-coroutines-debug-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.5.5/kotest-framework-discovery-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.3/mockk-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.5.5/kotest-extensions-jvm-5.5.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-jvm/1.13.1/mockk-jvm-1.13.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.13.1/mockk-dsl-jvm-1.13.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.13.1/mockk-agent-jvm-1.13.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.2/objenesis-3.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api-jvm/1.13.1/mockk-agent-api-jvm-1.13.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-core-jvm/1.13.1/mockk-core-jvm-1.13.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.13.2/junit-4.13.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.8.2/junit-jupiter-5.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.8.2/junit-jupiter-params-5.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.8.2/junit-jupiter-engine-5.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.6.4/kotlinx-coroutines-core-1.6.4.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.5.5/kotest-framework-concurrency-jvm-5.5.5.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
|
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
@ -1,8 +1,8 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.4" />
|
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.4/kotlinx-cli-jvm-0.3.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.5/kotlinx-cli-jvm-0.3.5.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
6
.idea/libraries/slf4j_simple.xml
generated
6
.idea/libraries/slf4j_simple.xml
generated
@ -1,9 +1,9 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="slf4j.simple" type="repository">
|
<library name="slf4j.simple" type="repository">
|
||||||
<properties maven-id="org.slf4j:slf4j-simple:1.7.36" />
|
<properties maven-id="org.slf4j:slf4j-simple:2.0.7" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.7/slf4j-simple-2.0.7.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
19
.idea/libraries/takes.xml
generated
19
.idea/libraries/takes.xml
generated
@ -1,15 +1,16 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="takes" type="repository">
|
<library name="takes" type="repository">
|
||||||
<properties maven-id="org.takes:takes:1.20" />
|
<properties maven-id="org.takes:takes:1.24.4" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
@ -7,13 +7,9 @@ version: 2
|
|||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
# Set the version of Python and other tools you might need
|
||||||
build:
|
build:
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
tools:
|
tools:
|
||||||
python: "3.9"
|
python: "3.11"
|
||||||
# You can also specify other tool versions:
|
|
||||||
# nodejs: "16"
|
|
||||||
# rust: "1.55"
|
|
||||||
# golang: "1.17"
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
# Build documentation in the docs/ directory with Sphinx
|
||||||
sphinx:
|
sphinx:
|
||||||
|
@ -15,14 +15,26 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val flat: Map<String, StNode> by lazy {
|
private var cachedFlat: Map<String, StNode>? = null
|
||||||
|
|
||||||
|
val flat: Map<String, StNode> get() {
|
||||||
|
if(cachedFlat!=null)
|
||||||
|
return cachedFlat!!
|
||||||
|
|
||||||
val result = mutableMapOf<String, StNode>()
|
val result = mutableMapOf<String, StNode>()
|
||||||
fun flatten(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
result[node.scopedName] = node
|
for(child in node.children) {
|
||||||
node.children.values.forEach { flatten(it) }
|
result[child.value.scopedName] = child.value
|
||||||
|
collect(child.value)
|
||||||
}
|
}
|
||||||
children.values.forEach { flatten(it) }
|
}
|
||||||
result
|
collect(this)
|
||||||
|
cachedFlat = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetCachedFlat() {
|
||||||
|
cachedFlat = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val allVariables: Collection<StStaticVariable> by lazy {
|
val allVariables: Collection<StStaticVariable> by lazy {
|
||||||
@ -201,6 +213,11 @@ class StMemVar(name: String,
|
|||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
astNode: PtNode) :
|
astNode: PtNode) :
|
||||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||||
|
|
||||||
|
init{
|
||||||
|
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||||
|
require(length!=null) { "memory mapped array or string must have known length" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StMemorySlab(
|
class StMemorySlab(
|
||||||
|
@ -26,11 +26,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY),
|
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
|
||||||
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY)
|
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
|
||||||
).forEach {
|
).forEach {
|
||||||
it.parent = program
|
it.parent = program
|
||||||
st.add(StMemVar(it.name, it.type, it.address, null, it))
|
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,13 @@ class PtFunctionCall(val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position)
|
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "[PtIdentifier:$name $type $position]"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copy() = PtIdentifier(name, type, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||||
@ -215,6 +221,8 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||||
|
|
||||||
|
override fun toString() = "PtNumber:$type:$number"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
||||||
* passing it as a String to the specified receiver function.
|
* passing it as a String to the specified receiver function.
|
||||||
*/
|
*/
|
||||||
fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
|
||||||
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||||
fun txt(node: PtNode): String {
|
fun txt(node: PtNode): String {
|
||||||
return when(node) {
|
return when(node) {
|
||||||
@ -134,19 +134,25 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
root.children.forEach {
|
root.children.forEach {
|
||||||
walkAst(it) { node, depth ->
|
walkAst(it) { node, depth ->
|
||||||
val txt = txt(node)
|
val txt = txt(node)
|
||||||
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
|
if(!library || !skipLibraries) {
|
||||||
if (txt.isNotEmpty())
|
if (txt.isNotEmpty())
|
||||||
output(" ".repeat(depth) + txt(node))
|
output(" ".repeat(depth) + txt(node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
println()
|
println()
|
||||||
} else {
|
} else {
|
||||||
walkAst(root) { node, depth ->
|
walkAst(root) { node, depth ->
|
||||||
val txt = txt(node)
|
val txt = txt(node)
|
||||||
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
|
if(!library || !skipLibraries) {
|
||||||
if (txt.isNotEmpty())
|
if (txt.isNotEmpty())
|
||||||
output(" ".repeat(depth) + txt(node))
|
output(" ".repeat(depth) + txt(node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
|
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
|
||||||
fun recurse(node: PtNode, depth: Int) {
|
fun recurse(node: PtNode, depth: Int) {
|
||||||
|
@ -38,25 +38,17 @@ class PtSub(
|
|||||||
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
||||||
|
|
||||||
|
|
||||||
class PtAssignment(position: Position) : PtNode(position) {
|
sealed interface IPtAssignment {
|
||||||
|
val children: MutableList<PtNode>
|
||||||
val target: PtAssignTarget
|
val target: PtAssignTarget
|
||||||
get() = children[0] as PtAssignTarget
|
get() = children[0] as PtAssignTarget
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
||||||
|
|
||||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) {
|
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||||
val target: PtAssignTarget
|
|
||||||
get() = children[0] as PtAssignTarget
|
|
||||||
val value: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
init {
|
|
||||||
require(operator.endsWith('=') || operator in PrefixOperators) {
|
|
||||||
"invalid augmented assign operator $operator"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||||
@ -100,8 +92,8 @@ class PtForLoop(position: Position) : PtNode(position) {
|
|||||||
|
|
||||||
|
|
||||||
class PtIfElse(position: Position) : PtNode(position) {
|
class PtIfElse(position: Position) : PtNode(position) {
|
||||||
val condition: PtBinaryExpression
|
val condition: PtExpression
|
||||||
get() = children[0] as PtBinaryExpression
|
get() = children[0] as PtExpression
|
||||||
val ifScope: PtNodeGroup
|
val ifScope: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
val elseScope: PtNodeGroup
|
val elseScope: PtNodeGroup
|
||||||
|
@ -79,12 +79,15 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
// cmp returns a status in the carry flag, but not a proper return value
|
// cmp returns a status in the carry flag, but not a proper return value
|
||||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||||
|
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||||
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||||
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
|
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||||
|
"divmodw" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
|
@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var asmListfile: Boolean = false,
|
var asmListfile: Boolean = false,
|
||||||
var experimentalCodegen: Boolean = false,
|
var experimentalCodegen: Boolean = false,
|
||||||
var varsHigh: Boolean = false,
|
var varsHigh: Boolean = false,
|
||||||
|
var useNewExprCode: Boolean = false,
|
||||||
var evalStackBaseAddress: UInt? = null,
|
var evalStackBaseAddress: UInt? = null,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
|
@ -13,7 +13,5 @@ interface ICodeGeneratorBackend {
|
|||||||
|
|
||||||
interface IAssemblyProgram {
|
interface IAssemblyProgram {
|
||||||
val name: String
|
val name: String
|
||||||
fun assemble(options: CompilationOptions): Boolean
|
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
import kotlin.io.path.absolute
|
||||||
@ -7,6 +8,10 @@ import kotlin.io.path.absolute
|
|||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
fun toClickableStr(): String {
|
fun toClickableStr(): String {
|
||||||
|
if(this===DUMMY)
|
||||||
|
return ""
|
||||||
|
if(file.startsWith(libraryFilePrefix))
|
||||||
|
return "$file:$line:$startCol:"
|
||||||
return try {
|
return try {
|
||||||
val path = Path(file).absolute().normalize().toString()
|
val path = Path(file).absolute().normalize().toString()
|
||||||
"file://$path:$line:$startCol:"
|
"file://$path:$line:$startCol:"
|
||||||
|
@ -16,5 +16,7 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Cb
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c64"
|
const val NAME = "c64"
|
||||||
|
|
||||||
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.output == OutputType.XEX)
|
return if (compilerOptions.output == OutputType.XEX)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.c128
|
package prog8.code.target.c128
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ class C128MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println("\nStarting C-128 emulator x128...")
|
println("\nStarting C-128 emulator x128...")
|
||||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.c64
|
package prog8.code.target.c64
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -42,7 +43,7 @@ class C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
@ -44,7 +44,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zeropage locations used for floating point operations from the free list
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
0x03, 0x04, 0x10, 0x11, 0x12,
|
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
||||||
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.cx16
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
emulator = "box16"
|
emulator = "box16"
|
||||||
extraArgs = listOf("-sym", viceMonListName(programNameWithPath.toString()))
|
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
|
@ -30,7 +30,7 @@ dependencies {
|
|||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -311,20 +311,9 @@ private fun optimizeSameAssignments(
|
|||||||
/*
|
/*
|
||||||
sta A ; or stz double store, remove this first one
|
sta A ; or stz double store, remove this first one
|
||||||
sta A ; or stz
|
sta A ; or stz
|
||||||
|
However, this cannot be done relyably because 'A' could be a constant symbol referring to an I/O address.
|
||||||
|
We can't see that here and would otherwise delete valid double stores.
|
||||||
*/
|
*/
|
||||||
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
|
|
||||||
if(first[2]==second[2]) {
|
|
||||||
val firstvalue = first.substring(4)
|
|
||||||
val secondvalue = second.substring(4)
|
|
||||||
if(firstvalue==secondvalue) {
|
|
||||||
val address = getAddressArg(first, symbolTable)
|
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
|
||||||
overlappingMods = true
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
|
@ -4,6 +4,7 @@ import com.github.michaelbull.result.Ok
|
|||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.mapError
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -19,10 +20,10 @@ internal class AssemblyProgram(
|
|||||||
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||||
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
|
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||||
private val listFile = outputDir.resolve("$name.list")
|
private val listFile = outputDir.resolve("$name.list")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
|
|
||||||
val assemblerCommand: List<String>
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
|
||||||
|
"divmod" -> funcDivmod(fcall)
|
||||||
|
"divmodw" -> funcDivmodW(fcall)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
"ror" -> funcRor(fcall)
|
"ror" -> funcRor(fcall)
|
||||||
@ -70,12 +72,54 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"rrestorex" -> funcRrestoreX()
|
"rrestorex" -> funcRrestoreX()
|
||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall)
|
"callfar" -> funcCallFar(fcall)
|
||||||
|
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack)
|
||||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun funcDivmod(fcall: PtBuiltinFunctionCall) {
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.Y, false)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A ,false)
|
||||||
|
// math.divmod_ub_asm: -- divide A by Y, result quotient in Y, remainder in A (unsigned)
|
||||||
|
asmgen.out(" jsr math.divmod_ub_asm")
|
||||||
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
|
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
|
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||||
|
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A)
|
||||||
|
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||||
|
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
|
||||||
|
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
||||||
|
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||||
|
asmgen.out(" jsr math.divmod_uw_asm")
|
||||||
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
|
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
|
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
sta $remainderVar
|
||||||
|
sty $remainderVar+1""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, false)
|
||||||
|
asmgen.out(" jsr prog8_lib.strcmp_mem")
|
||||||
|
if(resultToStack)
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
private fun funcRsave() {
|
private fun funcRsave() {
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -163,24 +207,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
if(arg1.isSimple()) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple()) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +253,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple()) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy P8ZP_SCRATCH_W1+1
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
@ -218,7 +262,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -246,11 +290,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||||
val target =
|
val target =
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null, fcall.position)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
@ -259,7 +303,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,14 +594,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
if(arrayvar.type==DataType.UWORD) {
|
if(arrayvar.type==DataType.UWORD) {
|
||||||
if(dt!='b')
|
if(dt!='b')
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||||
} else {
|
} else {
|
||||||
val addressOf = PtAddressOf(arrayvar.position)
|
val addressOf = PtAddressOf(arrayvar.position)
|
||||||
addressOf.add(arrayvar)
|
addressOf.add(arrayvar)
|
||||||
addressOf.parent = arrayvar.parent.parent
|
addressOf.parent = arrayvar.parent.parent
|
||||||
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||||
}
|
}
|
||||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
@ -581,7 +625,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,7 +646,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -625,7 +669,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,15 +706,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
require(!asmgen.options.useNewExprCode)
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
val pointer = result?.first as? PtIdentifier
|
||||||
// pointervar is already in the zero page, no need to copy
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
// can do ZP,Y indexing
|
||||||
|
val varname = asmgen.asmVariableName(pointer)
|
||||||
|
val scope = fcall.definingISub()!!
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
|
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
val index = (addrExpr.right as PtNumber).number.toHex()
|
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
|
||||||
sta ($varname),y
|
sta ($varname),y
|
||||||
txa
|
txa
|
||||||
iny
|
iny
|
||||||
@ -679,16 +727,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else -> { /* fall through */ }
|
||||||
else -> throw AssemblyError("wrong pokew arg type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
|
// fall through method:
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
fun fallback() {
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||||
|
}
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
@ -714,38 +766,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
tay
|
tay
|
||||||
pla""")
|
pla""")
|
||||||
}
|
}
|
||||||
} else {
|
} else fallback()
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
require(!asmgen.options.useNewExprCode)
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
val pointer = result?.first as? PtIdentifier
|
||||||
// pointervar is already in the zero page, no need to copy
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
val index = (addrExpr.right as PtNumber).number.toHex()
|
// can do ZP,Y indexing
|
||||||
|
val varname = asmgen.asmVariableName(pointer)
|
||||||
|
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
pha
|
pha
|
||||||
iny
|
iny
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
tay
|
tay
|
||||||
pla""")
|
pla""")
|
||||||
} else {
|
} else fallback()
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
}
|
||||||
|
else -> fallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resultToStack){
|
if(resultToStack){
|
||||||
@ -976,7 +1016,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val addr = PtAddressOf(value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
addr.add(variable)
|
addr.add(variable)
|
||||||
addr.parent = call
|
addr.parent = call
|
||||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
|
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1002,9 +1042,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
conv.reg != null -> {
|
conv.reg != null -> {
|
||||||
val src = when (conv.dt) {
|
val src = when (conv.dt) {
|
||||||
@ -1020,9 +1060,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callconv")
|
else -> throw AssemblyError("callconv")
|
||||||
}
|
}
|
||||||
|
@ -33,14 +33,19 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
is PtIdentifier -> translateExpression(expression)
|
is PtIdentifier -> translateExpression(expression)
|
||||||
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||||
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||||
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
is PtContainmentCheck -> translateContainmentCheck(expression)
|
||||||
is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
|
||||||
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
|
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
|
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
|
||||||
else -> TODO("missing expression asmgen for $expression")
|
else -> TODO("missing expression asmgen for $expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateContainmentCheck(check: PtContainmentCheck) {
|
||||||
|
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
||||||
// only for use in nested expression evaluation
|
// only for use in nested expression evaluation
|
||||||
|
|
||||||
@ -240,17 +245,53 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: PtBinaryExpression) {
|
private fun translateExpression(expr: PtBinaryExpression) {
|
||||||
|
require(!asmgen.options.useNewExprCode)
|
||||||
|
|
||||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||||
|
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||||
|
return
|
||||||
|
|
||||||
val leftDt = expr.left.type
|
val leftDt = expr.left.type
|
||||||
val rightDt = expr.right.type
|
val rightDt = expr.right.type
|
||||||
// see if we can apply some optimized routines still
|
|
||||||
when(expr.operator) {
|
// compare with zero
|
||||||
|
if(expr.operator in ComparisonOperators) {
|
||||||
|
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||||
|
val rightVal = expr.right.asConstInteger()
|
||||||
|
if(rightVal==0)
|
||||||
|
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
|
||||||
|
return translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||||
|
|
||||||
|
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||||
|
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||||
|
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||||
|
|
||||||
|
// the general, non-optimized cases
|
||||||
|
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
translateExpressionInternal(expr.right)
|
||||||
|
when (leftDt) {
|
||||||
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
|
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||||
|
else -> throw AssemblyError("non-numerical datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
|
||||||
|
val leftDt = left.type
|
||||||
|
val rightDt = right.type
|
||||||
|
when(operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVal = expr.left.asConstInteger()
|
val leftVal = left.asConstInteger()
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if (leftVal!=null && leftVal in -4..4) {
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
translateExpressionInternal(expr.right)
|
translateExpressionInternal(right)
|
||||||
if(rightDt in ByteDatatypes) {
|
if(rightDt in ByteDatatypes) {
|
||||||
val incdec = if(leftVal<0) "dec" else "inc"
|
val incdec = if(leftVal<0) "dec" else "inc"
|
||||||
repeat(leftVal.absoluteValue) {
|
repeat(leftVal.absoluteValue) {
|
||||||
@ -276,11 +317,11 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
else if (rightVal!=null && rightVal in -4..4)
|
else if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "dec" else "inc"
|
val incdec = if(rightVal<0) "dec" else "inc"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -306,16 +347,16 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if (rightVal!=null && rightVal in -4..4)
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "inc" else "dec"
|
val incdec = if(rightVal<0) "inc" else "dec"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -341,14 +382,14 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
val amount = expr.right.asConstInteger()
|
val amount = right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
@ -374,17 +415,17 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_right_uw_7")
|
asmgen.out(" jsr math.shift_right_uw_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount>=16) {
|
if(amount>=16) {
|
||||||
@ -399,27 +440,27 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
sta P8ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
sta P8ESTACK_HI+1,x
|
sta P8ESTACK_HI+1,x
|
||||||
+""")
|
+""")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_right_w_7")
|
asmgen.out(" jsr math.shift_right_w_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_w_$left")
|
asmgen.out(" jsr math.shift_right_w_$amountLeft")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
val amount = expr.right.asConstInteger()
|
val amount = right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||||
@ -429,78 +470,80 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_left_w_7")
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_left_w_$left")
|
asmgen.out(" jsr math.shift_left_w_$amountLeft")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVar = expr.left as? PtIdentifier
|
val leftVar = left as? PtIdentifier
|
||||||
val rightVar = expr.right as? PtIdentifier
|
val rightVar = right as? PtIdentifier
|
||||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
|
||||||
return translateSquared(leftVar, leftDt)
|
translateSquared(leftVar, leftDt)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val value = expr.right as? PtNumber
|
val value = right as? PtNumber
|
||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
if(rightDt in IntegerDatatypes) {
|
if(rightDt in IntegerDatatypes) {
|
||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
if(amount==2) {
|
if(amount==2) {
|
||||||
// optimize x*2 common case
|
// optimize x*2 common case
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
when(rightDt) {
|
when(rightDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
@ -510,9 +553,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if(rightVal!=null && rightVal==2) {
|
if(rightVal!=null && rightVal==2) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
@ -545,38 +588,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in ComparisonOperators -> {
|
|
||||||
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
|
||||||
val rightVal = expr.right.asConstInteger()
|
|
||||||
if(rightVal==0)
|
|
||||||
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
return false
|
||||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
|
||||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
|
||||||
|
|
||||||
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
|
|
||||||
translateCompareStrings(expr.left, expr.operator, expr.right)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// the general, non-optimized cases
|
|
||||||
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
|
||||||
translateExpressionInternal(expr.left)
|
|
||||||
translateExpressionInternal(expr.right)
|
|
||||||
when (leftDt) {
|
|
||||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
|
||||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
|
||||||
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
|
||||||
else -> throw AssemblyError("non-numerical datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
||||||
@ -657,7 +675,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
"<=" -> {
|
"<=" -> {
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzeros_b")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
||||||
@ -871,9 +889,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
|
||||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
|
||||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||||
|
compareStringsProcessResultInA(operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compareStringsProcessResultInA(operator: String) {
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||||
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||||
|
@ -50,8 +50,8 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
|
|||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -68,8 +68,8 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
@ -594,6 +594,5 @@ $loopLabel""")
|
|||||||
asmgen.assignExpressionToVariable(
|
asmgen.assignExpressionToVariable(
|
||||||
range.from,
|
range.from,
|
||||||
asmgen.asmVariableName(stmt.variable),
|
asmgen.asmVariableName(stmt.variable),
|
||||||
stmt.variable.type,
|
stmt.variable.type)
|
||||||
stmt.definingISub())
|
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
||||||
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
|
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
|
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
|
||||||
@ -198,16 +198,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
register!!
|
register!!
|
||||||
if(requiredDt largerThan value.type) {
|
if(requiredDt largerThan value.type) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||||
} else {
|
} else {
|
||||||
|
val scope = value.definingISub()
|
||||||
val target: AsmAssignTarget =
|
val target: AsmAssignTarget =
|
||||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, register = register)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||||
else {
|
else {
|
||||||
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
||||||
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
|
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
|
||||||
}
|
}
|
||||||
val src = if(value.type in PassByReferenceDatatypes) {
|
val src = if(value.type in PassByReferenceDatatypes) {
|
||||||
if(value is PtIdentifier) {
|
if(value is PtIdentifier) {
|
||||||
@ -221,7 +222,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY))
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("; simple int arg(s) passed via register(s)")
|
asmgen.out("; simple int arg(s) passed via register(s)")
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
val dt = sub.parameters[0].type
|
val dt = sub.parameters[0].type
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||||
if(dt in ByteDatatypes)
|
if(dt in ByteDatatypes)
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||||
else
|
else
|
||||||
@ -347,8 +347,8 @@ internal class ProgramAndVarsGen(
|
|||||||
} else {
|
} else {
|
||||||
require(sub.parameters.size==2)
|
require(sub.parameters.size==2)
|
||||||
// 2 simple byte args, first in A, second in Y
|
// 2 simple byte args, first in A, second in Y
|
||||||
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target1)
|
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||||
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||||
}
|
}
|
||||||
@ -358,26 +358,29 @@ internal class ProgramAndVarsGen(
|
|||||||
sub.children.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
asmgen.out("; variables")
|
asmgen.out("; variables")
|
||||||
|
asmgen.out(" .section BSS")
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
DataType.UBYTE -> asmgen.out("$name .byte 0")
|
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||||
DataType.UWORD -> asmgen.out("$name .word 0")
|
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||||
else -> throw AssemblyError("weird dt")
|
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||||
asmgen.out("prog8_regsaveA .byte 0")
|
asmgen.out("prog8_regsaveA .byte ?")
|
||||||
if(asmGenInfo.usedRegsaveX)
|
if(asmGenInfo.usedRegsaveX)
|
||||||
asmgen.out("prog8_regsaveX .byte 0")
|
asmgen.out("prog8_regsaveX .byte ?")
|
||||||
if(asmGenInfo.usedRegsaveY)
|
if(asmGenInfo.usedRegsaveY)
|
||||||
asmgen.out("prog8_regsaveY .byte 0")
|
asmgen.out("prog8_regsaveY .byte ?")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
asmgen.out(" .send BSS")
|
||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
|
@ -28,6 +28,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
private val asmgen: AsmGen6502Internal,
|
private val asmgen: AsmGen6502Internal,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val scope: IPtSubroutine?,
|
val scope: IPtSubroutine?,
|
||||||
|
val position: Position,
|
||||||
private val variableAsmName: String? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: PtArrayIndexer? = null,
|
val array: PtArrayIndexer? = null,
|
||||||
val memory: PtMemoryByte? = null,
|
val memory: PtMemoryByte? = null,
|
||||||
@ -43,8 +44,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
asmgen.asmVariableName(array.variable)
|
asmgen.asmVariableName(array.variable)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignmentBase
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(register!=null && datatype !in NumericDatatypes)
|
if(register!=null && datatype !in NumericDatatypes)
|
||||||
throw AssemblyError("register must be integer or float type")
|
throw AssemblyError("register must be integer or float type")
|
||||||
@ -63,28 +62,28 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
if(reg.statusflag!=null)
|
if(reg.statusflag!=null)
|
||||||
throw AssemblyError("can't assign value to processor statusflag directly")
|
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||||
else
|
else
|
||||||
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, register=reg.registerOrPair, origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, target.position, register=reg.registerOrPair, origAstTarget = this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, target.position, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
}
|
}
|
||||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this)
|
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this)
|
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, pos: Position, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
|
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
RegisterOrPair.R2,
|
RegisterOrPair.R2,
|
||||||
@ -100,7 +99,29 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSameAs(left: PtExpression): Boolean =
|
||||||
|
when(kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
val scopedName: String = if('.' in asmVarname)
|
||||||
|
asmVarname
|
||||||
|
else {
|
||||||
|
val scopeName = (scope as? PtNamedNode)?.scopedName
|
||||||
|
if (scopeName == null) asmVarname else "$scopeName.$asmVarname"
|
||||||
|
}
|
||||||
|
left is PtIdentifier && left.name==scopedName
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
left is PtArrayIndexer && left isSameAs array!!
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
left isSameAs memory!!
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
package prog8.codegen.cpu6502.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
|
import prog8.codegen.cpu6502.ExpressionsAsmGen
|
||||||
import prog8.codegen.cpu6502.VariableAllocator
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: PtProgram,
|
internal class AssignmentAsmGen(private val program: PtProgram,
|
||||||
|
private val symbolTable: SymbolTable,
|
||||||
private val asmgen: AsmGen6502Internal,
|
private val asmgen: AsmGen6502Internal,
|
||||||
private val allocator: VariableAllocator) {
|
private val allocator: VariableAllocator) {
|
||||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||||
@ -16,19 +21,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
|
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
|
||||||
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
val assign = AsmAssignment(source, target, program.memsizer, assignment.position)
|
val assign = AsmAssignment(source, target, program.memsizer, assignment.position)
|
||||||
target.origAssign = assign
|
translateNormalAssignment(assign, assignment.definingISub())
|
||||||
translateNormalAssignment(assign)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translate(augmentedAssign: PtAugmentedAssign) {
|
fun translate(augmentedAssign: PtAugmentedAssign) {
|
||||||
val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen)
|
val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen)
|
||||||
val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target)
|
val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, augmentedAssign.position)
|
val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, augmentedAssign.position)
|
||||||
target.origAssign = assign
|
augmentableAsmGen.translate(assign, augmentedAssign.definingISub())
|
||||||
augmentableAsmGen.translate(assign)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateNormalAssignment(assign: AsmAssignment) {
|
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
when(assign.source.kind) {
|
when(assign.source.kind) {
|
||||||
SourceStorageKind.LITERALNUMBER -> {
|
SourceStorageKind.LITERALNUMBER -> {
|
||||||
// simple case: assign a constant number
|
// simple case: assign a constant number
|
||||||
@ -129,7 +132,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
SourceStorageKind.MEMORY -> {
|
SourceStorageKind.MEMORY -> {
|
||||||
fun assignViaExprEval(expression: PtExpression) {
|
fun assignViaExprEval(expression: PtExpression) {
|
||||||
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope)
|
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
@ -144,7 +147,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
|
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
|
require(!asmgen.options.useNewExprCode)
|
||||||
|
val addrExpr = value.address as PtBinaryExpression
|
||||||
|
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
} else {
|
} else {
|
||||||
assignViaExprEval(value.address)
|
assignViaExprEval(value.address)
|
||||||
@ -154,7 +159,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
assignExpression(assign)
|
assignExpression(assign, scope)
|
||||||
}
|
}
|
||||||
SourceStorageKind.REGISTER -> {
|
SourceStorageKind.REGISTER -> {
|
||||||
asmgen.assignRegister(assign.source.register!!, assign.target)
|
asmgen.assignRegister(assign.source.register!!, assign.target)
|
||||||
@ -166,7 +171,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignExpression(assign: AsmAssignment) {
|
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
when(val value = assign.source.expression!!) {
|
when(val value = assign.source.expression!!) {
|
||||||
is PtAddressOf -> {
|
is PtAddressOf -> {
|
||||||
val sourceName = asmgen.asmSymbolName(value.identifier)
|
val sourceName = asmgen.asmSymbolName(value.identifier)
|
||||||
@ -279,17 +284,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
AsmAssignment(
|
AsmAssignment(
|
||||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||||
assign.target, program.memsizer, assign.position
|
assign.target, program.memsizer, assign.position
|
||||||
)
|
), scope
|
||||||
)
|
)
|
||||||
when (value.operator) {
|
when (value.operator) {
|
||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> inplaceNegate(assign, true)
|
"-" -> inplaceNegate(assign, true, scope)
|
||||||
"~" -> inplaceInvert(assign)
|
"~" -> inplaceInvert(assign, scope)
|
||||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assignPrefixedExpressionToArrayElt(assign)
|
assignPrefixedExpressionToArrayElt(assign, scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtContainmentCheck -> {
|
is PtContainmentCheck -> {
|
||||||
@ -297,6 +302,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
|
require(!asmgen.options.useNewExprCode)
|
||||||
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
||||||
// All remaining binary expressions just evaluate via the stack for now.
|
// All remaining binary expressions just evaluate via the stack for now.
|
||||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||||
@ -308,7 +314,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) {
|
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
require(assign.source.expression is PtPrefix)
|
require(assign.source.expression is PtPrefix)
|
||||||
if(assign.source.datatype==DataType.FLOAT) {
|
if(assign.source.datatype==DataType.FLOAT) {
|
||||||
// floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array.
|
// floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array.
|
||||||
@ -318,9 +324,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
// array[x] = -value ... use a tempvar then store that back into the array.
|
// array[x] = -value ... use a tempvar then store that back into the array.
|
||||||
val tempvar = asmgen.getTempVarName(assign.target.datatype)
|
val tempvar = asmgen.getTempVarName(assign.target.datatype)
|
||||||
val assignToTempvar = AsmAssignment(assign.source,
|
val assignToTempvar = AsmAssignment(assign.source,
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope,
|
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
||||||
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||||
asmgen.translateNormalAssignment(assignToTempvar)
|
asmgen.translateNormalAssignment(assignToTempvar, scope)
|
||||||
when(assign.target.datatype) {
|
when(assign.target.datatype) {
|
||||||
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
|
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
|
||||||
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
|
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
|
||||||
@ -342,6 +348,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
|
require(!asmgen.options.useNewExprCode)
|
||||||
if(expr.operator in ComparisonOperators) {
|
if(expr.operator in ComparisonOperators) {
|
||||||
if(expr.right.asConstInteger() == 0) {
|
if(expr.right.asConstInteger() == 0) {
|
||||||
if(expr.operator == "==" || expr.operator=="!=") {
|
if(expr.operator == "==" || expr.operator=="!=") {
|
||||||
@ -386,7 +393,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
else {
|
else {
|
||||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
@ -408,7 +415,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
|
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
|
||||||
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
|
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
|
||||||
@ -441,7 +448,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
expr.left.isSimple() && expr.right.isSimple()) {
|
expr.left.isSimple() && expr.right.isSimple()) {
|
||||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
if(expr.operator=="==") {
|
if(expr.operator=="==") {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -467,7 +474,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
if(expr.operator=="==") {
|
if(expr.operator=="==") {
|
||||||
@ -487,7 +494,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
cpy P8ZP_SCRATCH_W1+1
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
bne +
|
bne +
|
||||||
lda #0
|
lda #0
|
||||||
bne ++
|
beq ++
|
||||||
+ lda #1
|
+ lda #1
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
@ -724,6 +731,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
|
require(!asmgen.options.useNewExprCode)
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
"==" -> {
|
"==" -> {
|
||||||
when(val dt = expr.left.type) {
|
when(val dt = expr.left.type) {
|
||||||
@ -818,7 +826,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
// use subroutine
|
// use subroutine
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
val stringVal = (variable as PtVariable).value as PtString
|
val stringVal = (variable as PtVariable).value as PtString
|
||||||
asmgen.out(" ldy #${stringVal.value.length}")
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
@ -831,15 +839,15 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt, containment.definingISub())
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), "P8ZP_SCRATCH_W2"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname)
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
return
|
return
|
||||||
@ -888,7 +896,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
if(targetDt in WordDatatypes) {
|
if(targetDt in WordDatatypes) {
|
||||||
|
|
||||||
fun assignViaExprEval(addressExpression: PtExpression) {
|
fun assignViaExprEval(addressExpression: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out(" ldy #0")
|
asmgen.out(" ldy #0")
|
||||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
@ -903,7 +911,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
|
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
|
require(!asmgen.options.useNewExprCode)
|
||||||
|
val addrExpr = value.address as PtBinaryExpression
|
||||||
|
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||||
asmgen.out(" ldy #0")
|
asmgen.out(" ldy #0")
|
||||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
} else {
|
} else {
|
||||||
@ -942,7 +952,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
in PassByReferenceDatatypes -> {
|
in PassByReferenceDatatypes -> {
|
||||||
// str/array value cast (most likely to UWORD, take address-of)
|
// str/array value cast (most likely to UWORD, take address-of)
|
||||||
assignExpressionToVariable(value, target.asmVarname, targetDt, null)
|
assignExpressionToVariable(value, target.asmVarname, targetDt)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt --> $targetDt")
|
else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt --> $targetDt")
|
||||||
}
|
}
|
||||||
@ -1000,7 +1010,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
// have to typecast the float number on the fly down to an integer
|
// have to typecast the float number on the fly down to an integer
|
||||||
assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
|
assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
|
||||||
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
|
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
|
||||||
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes)
|
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, origTypeCastExpression.definingISub(), target.position)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if(!(valueDt isAssignableTo targetDt)) {
|
if(!(valueDt isAssignableTo targetDt)) {
|
||||||
@ -1105,7 +1115,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
lsb.add(value)
|
lsb.add(value)
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||||
val assign = AsmAssignment(src, target, program.memsizer, value.position)
|
val assign = AsmAssignment(src, target, program.memsizer, value.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign, value.definingISub())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
||||||
@ -1486,7 +1496,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.A -> asmgen.out(" inx | lda P8ESTACK_LO,x")
|
RegisterOrPair.A -> asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||||
RegisterOrPair.X -> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
RegisterOrPair.X -> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.position}")
|
||||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy P8ESTACK_LO,x")
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy P8ESTACK_LO,x")
|
||||||
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
||||||
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
||||||
@ -1505,9 +1515,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD, in PassByReferenceDatatypes -> {
|
DataType.UWORD, DataType.WORD, in PassByReferenceDatatypes -> {
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.AX -> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
RegisterOrPair.AX -> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.position}")
|
||||||
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
||||||
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.position}")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(
|
asmgen.out(
|
||||||
"""
|
"""
|
||||||
@ -1630,7 +1640,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}")
|
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
target.array!!
|
target.array!!
|
||||||
@ -2054,7 +2064,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
// we make an exception in the type check for assigning something to a register pair AX, AY or XY
|
// we make an exception in the type check for assigning something to a register pair AX, AY or XY
|
||||||
// these will be correctly typecasted from a byte to a word value here
|
// these will be correctly typecasted from a byte to a word value here
|
||||||
if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY))
|
if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY))
|
||||||
require(target.datatype in ByteDatatypes) { "assign target must be byte type ${target.origAstTarget?.position ?: ""}"}
|
require(target.datatype in ByteDatatypes) { "assign target must be byte type ${target.position}"}
|
||||||
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -2144,7 +2154,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
||||||
require(target.datatype in NumericDatatypes || target.datatype in PassByReferenceDatatypes) {
|
require(target.datatype in NumericDatatypes || target.datatype in PassByReferenceDatatypes) {
|
||||||
"assign target must be word type ${target.origAstTarget?.position ?: ""}"
|
"assign target must be word type ${target.position}"
|
||||||
}
|
}
|
||||||
if(target.datatype==DataType.FLOAT)
|
if(target.datatype==DataType.FLOAT)
|
||||||
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
|
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
|
||||||
@ -2350,7 +2360,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
|
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
||||||
@ -2514,7 +2524,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
|
internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
|
||||||
if (float == 0.0) {
|
if (float == 0.0) {
|
||||||
// optimized case for float zero
|
// optimized case for float zero
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
@ -2658,7 +2668,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
throw AssemblyError("no asm gen for assign memory byte at $address to array ${target.asmVarname}")
|
asmgen.out(" lda ${address.toHex()}")
|
||||||
|
assignRegisterByte(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> when(target.register!!) {
|
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||||
RegisterOrPair.A -> asmgen.out(" lda ${address.toHex()}")
|
RegisterOrPair.A -> asmgen.out(" lda ${address.toHex()}")
|
||||||
@ -2697,7 +2708,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${target.asmVarname} ")
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
|
assignRegisterByte(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
asmgen.loadByteFromPointerIntoA(identifier)
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
@ -2739,7 +2751,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
|
asmgen.out(" lda ${address.toHex()} | ldy #0")
|
||||||
|
assignRegisterpairWord(wordtarget, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> when(wordtarget.register!!) {
|
TargetStorageKind.REGISTER -> when(wordtarget.register!!) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}")
|
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}")
|
||||||
@ -2767,7 +2780,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
|
asmgen.out(" ldy #0")
|
||||||
|
assignRegisterpairWord(wordtarget, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
asmgen.loadByteFromPointerIntoA(identifier)
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
@ -2798,13 +2813,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
fun storeViaExprEval() {
|
fun storeViaExprEval() {
|
||||||
when(addressExpr) {
|
when(addressExpr) {
|
||||||
is PtNumber, is PtIdentifier -> {
|
is PtNumber, is PtIdentifier -> {
|
||||||
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// same as above but we need to save the A register
|
// same as above but we need to save the A register
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
}
|
}
|
||||||
@ -2819,7 +2834,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
asmgen.storeAIntoPointerVar(addressExpr)
|
asmgen.storeAIntoPointerVar(addressExpr)
|
||||||
}
|
}
|
||||||
addressExpr is PtBinaryExpression -> {
|
addressExpr is PtBinaryExpression -> {
|
||||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
require(!asmgen.options.useNewExprCode)
|
||||||
|
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
||||||
storeViaExprEval()
|
storeViaExprEval()
|
||||||
}
|
}
|
||||||
else -> storeViaExprEval()
|
else -> storeViaExprEval()
|
||||||
@ -2828,30 +2844,30 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) {
|
internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, expr.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign, expr.definingISub())
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) {
|
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType) {
|
||||||
if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) {
|
if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) {
|
||||||
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
|
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
|
||||||
} else {
|
} else {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, expr.definingISub(), expr.position, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign, expr.definingISub())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, pos: Position) {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, pos, null, asmgen)
|
||||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY)
|
val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun inplaceInvert(assign: AsmAssignment) {
|
internal fun inplaceInvert(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
val target = assign.target
|
val target = assign.target
|
||||||
when (assign.target.datatype) {
|
when (assign.target.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -2878,7 +2894,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.out(" sta ($sourceName),y")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
|
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (P8ZP_SCRATCH_W2),y
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
@ -2896,7 +2912,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
||||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
|
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2921,7 +2937,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
||||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
|
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2929,7 +2945,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
|
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||||
val target = assign.target
|
val target = assign.target
|
||||||
val datatype = if(ignoreDatatype) {
|
val datatype = if(ignoreDatatype) {
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
@ -2964,7 +2980,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
||||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
|
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3024,7 +3040,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
|
||||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
|
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3046,7 +3062,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
||||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
|
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||||
else -> throw AssemblyError("weird target for in-place float negation")
|
else -> throw AssemblyError("weird target for in-place float negation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -34,11 +34,15 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
val warnings = mutableListOf<String>()
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
override fun err(msg: String, position: Position) {
|
override fun err(msg: String, position: Position) {
|
||||||
errors.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in errors)
|
||||||
|
errors.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun warn(msg: String, position: Position) {
|
override fun warn(msg: String, position: Position) {
|
||||||
warnings.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in warnings)
|
||||||
|
warnings.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
@ -2,10 +2,7 @@ package prog8.codegen.experimental
|
|||||||
|
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.code.core.ICodeGeneratorBackend
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
|
|
||||||
@ -16,6 +13,7 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
options: CompilationOptions,
|
options: CompilationOptions,
|
||||||
errors: IErrorReporter
|
errors: IErrorReporter
|
||||||
): IAssemblyProgram? {
|
): IAssemblyProgram? {
|
||||||
|
|
||||||
// you could write a code generator directly on the PtProgram AST,
|
// you could write a code generator directly on the PtProgram AST,
|
||||||
// but you can also use the Intermediate Representation to build a codegen on:
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
|
@ -30,7 +30,7 @@ dependencies {
|
|||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -16,32 +16,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return translateRegularAssign(assignment)
|
return translateRegularAssign(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
|
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
if(augmentedAssign.target.children.single() is PtMachineRegister)
|
if(augAssign.target.children.single() is PtMachineRegister)
|
||||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||||
|
|
||||||
return translateInplaceAssign(augmentedAssign)
|
val ident = augAssign.target.identifier
|
||||||
}
|
val memory = augAssign.target.memory
|
||||||
|
val array = augAssign.target.array
|
||||||
private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks {
|
|
||||||
val ident = assignment.target.identifier
|
|
||||||
val memory = assignment.target.memory
|
|
||||||
val array = assignment.target.array
|
|
||||||
|
|
||||||
return if(ident!=null) {
|
return if(ident!=null) {
|
||||||
assignVarAugmented(ident.name, assignment)
|
assignVarAugmented(ident.name, augAssign)
|
||||||
} else if(memory != null) {
|
} else if(memory != null) {
|
||||||
if(memory.address is PtNumber)
|
if(memory.address is PtNumber)
|
||||||
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
|
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
|
||||||
else
|
else
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
} else if(array!=null) {
|
} else if(array!=null) {
|
||||||
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
||||||
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
||||||
// will be optimized later and have the double assignments removed.
|
// will be optimized later and have the double assignments removed.
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
} else {
|
} else {
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,25 +57,40 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||||
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||||
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
"%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value)
|
||||||
|
"==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value)
|
||||||
|
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value)
|
||||||
|
"<" -> expressionEval.operatorLessInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
">" -> expressionEval.operatorGreaterInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
"<=" -> expressionEval.operatorLessEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
||||||
|
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||||
val value = assignment.value
|
val value = assignment.value
|
||||||
val valueVmDt = codeGen.irType(value.type)
|
val targetDt = codeGen.irType(assignment.target.type)
|
||||||
return when (assignment.operator) {
|
return when (assignment.operator) {
|
||||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value)
|
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
|
||||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value)
|
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
|
||||||
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value)
|
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
|
||||||
"/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
"|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value)
|
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
|
||||||
"&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value)
|
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
|
||||||
"^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value)
|
"^=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
|
||||||
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value)
|
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
|
||||||
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
|
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
|
||||||
|
"==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value)
|
||||||
|
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
|
||||||
|
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,19 +98,36 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
if (codeGen.options.slowCodegenWarnings)
|
if (codeGen.options.slowCodegenWarnings)
|
||||||
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||||
val normalAssign = PtAssignment(origAssign.position)
|
|
||||||
normalAssign.add(origAssign.target)
|
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
if(origAssign.operator in PrefixOperators) {
|
if(origAssign.operator in PrefixOperators) {
|
||||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||||
value.add(origAssign.value)
|
value.add(origAssign.value)
|
||||||
} else {
|
} else {
|
||||||
require(origAssign.operator.endsWith('='))
|
require(origAssign.operator.endsWith('='))
|
||||||
|
if(codeGen.options.useNewExprCode) {
|
||||||
|
// X += Y -> temp = X, temp += Y, X = temp
|
||||||
|
val tempvar = codeGen.getReusableTempvar(origAssign.definingSub()!!, origAssign.target.type)
|
||||||
|
val assign = PtAssignment(origAssign.position)
|
||||||
|
val target = PtAssignTarget(origAssign.position)
|
||||||
|
target.add(tempvar)
|
||||||
|
assign.add(target)
|
||||||
|
assign.add(origAssign.target.children.single())
|
||||||
|
val augAssign = PtAugmentedAssign(origAssign.operator, origAssign.position)
|
||||||
|
augAssign.add(target)
|
||||||
|
augAssign.add(origAssign.value)
|
||||||
|
val assignBack = PtAssignment(origAssign.position)
|
||||||
|
assignBack.add(origAssign.target)
|
||||||
|
assignBack.add(tempvar)
|
||||||
|
return translateRegularAssign(assign) + translate(augAssign) + translateRegularAssign(assignBack)
|
||||||
|
} else {
|
||||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||||
value.add(left)
|
value.add(left)
|
||||||
value.add(origAssign.value)
|
value.add(origAssign.value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
val normalAssign = PtAssignment(origAssign.position)
|
||||||
|
normalAssign.add(origAssign.target)
|
||||||
normalAssign.add(value)
|
normalAssign.add(value)
|
||||||
return translateRegularAssign(normalAssign)
|
return translateRegularAssign(normalAssign)
|
||||||
}
|
}
|
||||||
@ -130,119 +158,126 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||||
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
||||||
val ident = assignment.target.identifier
|
val targetIdent = assignment.target.identifier
|
||||||
val memory = assignment.target.memory
|
val targetMemory = assignment.target.memory
|
||||||
val array = assignment.target.array
|
val targetArray = assignment.target.array
|
||||||
val vmDt = codeGen.irType(assignment.value.type)
|
val vmDt = codeGen.irType(assignment.value.type)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
var resultRegister = -1
|
|
||||||
var resultFpRegister = -1
|
var valueRegister = -1
|
||||||
|
var valueFpRegister = -1
|
||||||
val zero = codeGen.isZero(assignment.value)
|
val zero = codeGen.isZero(assignment.value)
|
||||||
if(!zero) {
|
if(!zero) {
|
||||||
// calculate the assignment value
|
// calculate the assignment value
|
||||||
if (vmDt == IRDataType.FLOAT) {
|
if (vmDt == IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.nextFreeFloat()
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
|
valueFpRegister = tr.resultFpReg
|
||||||
|
addToResult(result, tr, -1, valueFpRegister)
|
||||||
} else {
|
} else {
|
||||||
resultRegister = if (assignment.value is PtMachineRegister) {
|
if (assignment.value is PtMachineRegister) {
|
||||||
(assignment.value as PtMachineRegister).register
|
valueRegister = (assignment.value as PtMachineRegister).register
|
||||||
} else {
|
} else {
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
result += expressionEval.translateExpression(assignment.value, reg, -1)
|
valueRegister = tr.resultReg
|
||||||
reg
|
addToResult(result, tr, valueRegister, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ident!=null) {
|
|
||||||
|
if(targetIdent!=null) {
|
||||||
val instruction = if(zero) {
|
val instruction = if(zero) {
|
||||||
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
|
||||||
} else {
|
} else {
|
||||||
if (vmDt == IRDataType.FLOAT)
|
if (vmDt == IRDataType.FLOAT) {
|
||||||
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||||
|
}
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(null, null).also { it += instruction }
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else if(array!=null) {
|
else if(targetArray!=null) {
|
||||||
val variable = array.variable.name
|
val variable = targetArray.variable.name
|
||||||
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||||
|
|
||||||
if(array.variable.type==DataType.UWORD) {
|
if(targetArray.variable.type==DataType.UWORD) {
|
||||||
// indexing a pointer var instead of a real array or string
|
// indexing a pointer var instead of a real array or string
|
||||||
if(itemsize!=1)
|
if(itemsize!=1)
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
if(array.index.type!=DataType.UBYTE)
|
if(targetArray.index.type!=DataType.UBYTE)
|
||||||
throw AssemblyError("non-array var indexing requires bytes index")
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
val idxReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetArray.index)
|
||||||
result += expressionEval.translateExpression(array.index, idxReg, -1)
|
val idxReg = tr.resultReg
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
if(zero) {
|
if(zero) {
|
||||||
// there's no STOREZIX instruction
|
// there's no STOREZIX instruction
|
||||||
resultRegister = codeGen.registers.nextFree()
|
valueRegister = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, value=0)
|
||||||
}
|
}
|
||||||
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
|
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||||
result += code
|
result += code
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
val fixedIndex = constIntValue(array.index)
|
val fixedIndex = constIntValue(targetArray.index)
|
||||||
if(zero) {
|
if(zero) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(vmDt== IRDataType.FLOAT) {
|
if(vmDt== IRDataType.FLOAT) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else if(memory!=null) {
|
else if(targetMemory!=null) {
|
||||||
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.position}"}
|
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||||
if(zero) {
|
if(zero) {
|
||||||
if(memory.address is PtNumber) {
|
if(targetMemory.address is PtNumber) {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
val addressReg = tr.resultReg
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(memory.address is PtNumber) {
|
if(targetMemory.address is PtNumber) {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, value=(targetMemory.address as PtNumber).number.toInt()) }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
val addressReg = tr.resultReg
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,14 +287,29 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
throw AssemblyError("weird assigntarget")
|
throw AssemblyError("weird assigntarget")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
|
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
|
||||||
return if(itemsize==1) {
|
// returns the code to load the Index into the register, which is also return\ed.
|
||||||
expressionEval.translateExpression(array.index, indexReg, -1)
|
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(itemsize==1) {
|
||||||
|
val tr = expressionEval.translateExpression(array.index)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
return Pair(result, tr.resultReg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(codeGen.options.useNewExprCode) {
|
||||||
|
val tr = expressionEval.translateExpression(array.index)
|
||||||
|
result += tr.chunks
|
||||||
|
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, value = itemsize), null)
|
||||||
|
return Pair(result, tr.resultReg)
|
||||||
} else {
|
} else {
|
||||||
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
val mult: PtExpression
|
||||||
|
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
mult.children += array.index
|
mult.children += array.index
|
||||||
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
expressionEval.translateExpression(mult, indexReg, -1)
|
val tr = expressionEval.translateExpression(mult)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,14 +9,16 @@ import prog8.intermediate.*
|
|||||||
|
|
||||||
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
|
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
|
||||||
|
|
||||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
return when(call.name) {
|
return when(call.name) {
|
||||||
"any" -> funcAny(call, resultRegister)
|
"any" -> funcAny(call)
|
||||||
"all" -> funcAll(call, resultRegister)
|
"all" -> funcAll(call)
|
||||||
"abs" -> funcAbs(call, resultRegister)
|
"abs" -> funcAbs(call)
|
||||||
"cmp" -> funcCmp(call)
|
"cmp" -> funcCmp(call)
|
||||||
"sgn" -> funcSgn(call, resultRegister)
|
"sgn" -> funcSgn(call)
|
||||||
"sqrt16" -> funcSqrt16(call, resultRegister)
|
"sqrt16" -> funcSqrt16(call)
|
||||||
|
"divmod" -> funcDivmod(call, IRDataType.BYTE)
|
||||||
|
"divmodw" -> funcDivmod(call, IRDataType.WORD)
|
||||||
"pop" -> funcPop(call)
|
"pop" -> funcPop(call)
|
||||||
"popw" -> funcPopw(call)
|
"popw" -> funcPopw(call)
|
||||||
"push" -> funcPush(call)
|
"push" -> funcPush(call)
|
||||||
@ -24,40 +26,75 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
"rsave",
|
"rsave",
|
||||||
"rsavex",
|
"rsavex",
|
||||||
"rrestore",
|
"rrestore",
|
||||||
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
|
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||||
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
|
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
|
||||||
"msb" -> funcMsb(call, resultRegister)
|
"msb" -> funcMsb(call)
|
||||||
"lsb" -> funcLsb(call, resultRegister)
|
"lsb" -> funcLsb(call)
|
||||||
"memory" -> funcMemory(call, resultRegister)
|
"memory" -> funcMemory(call)
|
||||||
"peek" -> funcPeek(call, resultRegister)
|
"peek" -> funcPeek(call)
|
||||||
"peekw" -> funcPeekW(call, resultRegister)
|
"peekw" -> funcPeekW(call)
|
||||||
"poke" -> funcPoke(call)
|
"poke" -> funcPoke(call)
|
||||||
"pokew" -> funcPokeW(call)
|
"pokew" -> funcPokeW(call)
|
||||||
"pokemon" -> emptyList()
|
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
|
||||||
"mkword" -> funcMkword(call, resultRegister)
|
"mkword" -> funcMkword(call)
|
||||||
"sort" -> funcSort(call)
|
"sort" -> funcSort(call)
|
||||||
"reverse" -> funcReverse(call)
|
"reverse" -> funcReverse(call)
|
||||||
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
|
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||||
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
|
"ror" -> funcRolRor(Opcode.ROXR, call)
|
||||||
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
|
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||||
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
|
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||||
|
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
|
||||||
val leftRegister = codeGen.registers.nextFree()
|
|
||||||
val rightRegister = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], leftRegister, -1)
|
val number = call.args[0]
|
||||||
result += exprGen.translateExpression(call.args[1], rightRegister, -1)
|
val divident = call.args[1]
|
||||||
result += IRCodeChunk(null, null).also {
|
if(divident is PtNumber) {
|
||||||
it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
val tr = exprGen.translateExpression(number)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, value=divident.number.toInt()), null)
|
||||||
|
} else {
|
||||||
|
val numTr = exprGen.translateExpression(number)
|
||||||
|
addToResult(result, numTr, numTr.resultReg, -1)
|
||||||
|
val dividentTr = exprGen.translateExpression(divident)
|
||||||
|
addToResult(result, dividentTr, dividentTr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.DIVMODR, type, reg1 = numTr.resultReg, reg2=dividentTr.resultReg), null)
|
||||||
}
|
}
|
||||||
return result
|
// DIVMOD result convention: division in r0, remainder in r1
|
||||||
|
result += assignRegisterTo(call.args[2], 0)
|
||||||
|
result += assignRegisterTo(call.args[3], 1)
|
||||||
|
return ExpressionCodeResult(result, type, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val left = exprGen.translateExpression(call.args[0])
|
||||||
|
val right = exprGen.translateExpression(call.args[1])
|
||||||
|
val targetReg = codeGen.registers.nextFree()
|
||||||
|
addToResult(result, left, 65500, -1)
|
||||||
|
addToResult(result, right, 65501, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.SYSCALL, value=IMSyscall.COMPARE_STRINGS.number), null)
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=targetReg, reg2=0), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, targetReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val leftTr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
|
val dt = codeGen.irType(call.args[0].type)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||||
|
}
|
||||||
|
return ExpressionCodeResult(result, dt, leftTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
val syscall =
|
val syscall =
|
||||||
@ -70,17 +107,19 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type")
|
else -> throw IllegalArgumentException("weird type")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
if(resultRegister!=0)
|
// SysCall call convention: return value in register r0
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
if(tr.resultReg!=0)
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
val syscall =
|
val syscall =
|
||||||
@ -93,115 +132,124 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type")
|
else -> throw IllegalArgumentException("weird type")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
if(resultRegister!=0)
|
// SysCall call convention: return value in register r0
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
if(tr.resultReg!=0)
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val sourceDt = call.args.single().type
|
val sourceDt = call.args.single().type
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(sourceDt!=DataType.UWORD) {
|
if(sourceDt==DataType.UWORD)
|
||||||
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
return ExpressionCodeResult.EMPTY
|
||||||
|
|
||||||
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
when (sourceDt) {
|
when (sourceDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
|
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
val compareReg = codeGen.registers.nextFree()
|
val compareReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
||||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
|
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
|
||||||
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
|
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
|
||||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
|
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(notNegativeLabel, null)
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
val compareReg = codeGen.registers.nextFree()
|
val compareReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
||||||
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
|
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
|
||||||
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
|
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(notNegativeLabel, null)
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val reg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
val vmDt = codeGen.irType(call.type)
|
||||||
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
|
it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, vmDt, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcSqrt16(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val reg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
|
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val reg = codeGen.registers.nextFree()
|
val reg = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
||||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
result += assignRegisterTo(call.args.single(), reg)
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val reg = codeGen.registers.nextFree()
|
val reg = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
||||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
result += assignRegisterTo(call.args.single(), reg)
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
|
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
|
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
val syscall =
|
val syscall =
|
||||||
@ -212,15 +260,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type to reverse")
|
else -> throw IllegalArgumentException("weird type to reverse")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
val syscall =
|
val syscall =
|
||||||
@ -234,26 +283,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type to sort")
|
else -> throw IllegalArgumentException("weird type to sort")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val msbReg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], msbReg, -1)
|
val msbTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[1], resultRegister, -1)
|
addToResult(result, msbTr, msbTr.resultReg, -1)
|
||||||
|
val lsbTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(codeGen.isZero(call.args[1])) {
|
if(codeGen.isZero(call.args[1])) {
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
@ -262,33 +313,34 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
|
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val tr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
|
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, value = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val addressTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPoke(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(codeGen.isZero(call.args[1])) {
|
if(codeGen.isZero(call.args[1])) {
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
@ -297,97 +349,108 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val tr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, value = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val addressTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcPeekW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(call.args[0] is PtNumber) {
|
return if(call.args[0] is PtNumber) {
|
||||||
|
val resultRegister = codeGen.registers.nextFree()
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
|
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcPeek(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(call.args[0] is PtNumber) {
|
return if(call.args[0] is PtNumber) {
|
||||||
|
val resultRegister = codeGen.registers.nextFree()
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val name = (call.args[0] as PtString).value
|
val name = (call.args[0] as PtString).value
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
val resultReg = codeGen.registers.nextFree()
|
||||||
return listOf(code)
|
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||||
|
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
return exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
return exprGen.translateExpression(call.args.single())
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2 = resultRegister)
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val vmDt = codeGen.irType(call.args[0].type)
|
val vmDt = codeGen.irType(call.args[0].type)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
|
it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
result += assignRegisterTo(call.args[0], resultRegister)
|
result += assignRegisterTo(call.args[0], tr.resultReg)
|
||||||
return result
|
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ class IRCodeGen(
|
|||||||
private val expressionEval = ExpressionGen(this)
|
private val expressionEval = ExpressionGen(this)
|
||||||
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
||||||
private val assignmentGen = AssignmentGen(this, expressionEval)
|
private val assignmentGen = AssignmentGen(this, expressionEval)
|
||||||
|
private var irSymbolTable: IRSymbolTable = IRSymbolTable(null)
|
||||||
internal val registers = RegisterPool()
|
internal val registers = RegisterPool()
|
||||||
|
|
||||||
fun generate(): IRProgram {
|
fun generate(): IRProgram {
|
||||||
@ -28,7 +29,8 @@ class IRCodeGen(
|
|||||||
moveAllNestedSubroutinesToBlockScope()
|
moveAllNestedSubroutinesToBlockScope()
|
||||||
verifyNameScoping(program, symbolTable)
|
verifyNameScoping(program, symbolTable)
|
||||||
|
|
||||||
val irProg = IRProgram(program.name, IRSymbolTable(symbolTable), options, program.encoding)
|
irSymbolTable = IRSymbolTable(symbolTable)
|
||||||
|
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||||
|
|
||||||
if(options.evalStackBaseAddress!=null)
|
if(options.evalStackBaseAddress!=null)
|
||||||
throw AssemblyError("IR doesn't use eval-stack")
|
throw AssemblyError("IR doesn't use eval-stack")
|
||||||
@ -217,7 +219,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
program.allBlocks().forEach { block ->
|
program.allBlocks().forEach { block ->
|
||||||
block.children.forEach {
|
block.children.toList().forEach {
|
||||||
if (it is PtSub) {
|
if (it is PtSub) {
|
||||||
// Only regular subroutines can have nested subroutines.
|
// Only regular subroutines can have nested subroutines.
|
||||||
it.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, it, subsub) }
|
it.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, it, subsub) }
|
||||||
@ -238,8 +240,14 @@ class IRCodeGen(
|
|||||||
is PtAssignment -> assignmentGen.translate(node)
|
is PtAssignment -> assignmentGen.translate(node)
|
||||||
is PtAugmentedAssign -> assignmentGen.translate(node)
|
is PtAugmentedAssign -> assignmentGen.translate(node)
|
||||||
is PtNodeGroup -> translateGroup(node.children)
|
is PtNodeGroup -> translateGroup(node.children)
|
||||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
is PtBuiltinFunctionCall -> {
|
||||||
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
|
val result = translateBuiltinFunc(node)
|
||||||
|
result.chunks // it's not an expression so no result value.
|
||||||
|
}
|
||||||
|
is PtFunctionCall -> {
|
||||||
|
val result = expressionEval.translate(node)
|
||||||
|
result.chunks // it's not an expression so no result value
|
||||||
|
}
|
||||||
is PtNop -> emptyList()
|
is PtNop -> emptyList()
|
||||||
is PtReturn -> translate(node)
|
is PtReturn -> translate(node)
|
||||||
is PtJump -> translate(node)
|
is PtJump -> translate(node)
|
||||||
@ -358,6 +366,12 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
||||||
|
if(chunks.isEmpty()) {
|
||||||
|
return listOf(
|
||||||
|
IRCodeChunk(label, null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
require(chunks.isNotEmpty() && label.isNotBlank())
|
require(chunks.isNotEmpty() && label.isNotBlank())
|
||||||
val first = chunks[0]
|
val first = chunks[0]
|
||||||
if(first.label!=null) {
|
if(first.label!=null) {
|
||||||
@ -389,10 +403,9 @@ class IRCodeGen(
|
|||||||
if(whenStmt.choices.children.isEmpty())
|
if(whenStmt.choices.children.isEmpty())
|
||||||
return emptyList()
|
return emptyList()
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val valueReg = registers.nextFree()
|
|
||||||
val choiceReg = registers.nextFree()
|
|
||||||
val valueDt = irType(whenStmt.value.type)
|
val valueDt = irType(whenStmt.value.type)
|
||||||
result += expressionEval.translateExpression(whenStmt.value, valueReg, -1)
|
val valueTr = expressionEval.translateExpression(whenStmt.value)
|
||||||
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
|
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
|
||||||
val endLabel = createLabelName()
|
val endLabel = createLabelName()
|
||||||
for (choice in choices) {
|
for (choice in choices) {
|
||||||
@ -401,10 +414,11 @@ class IRCodeGen(
|
|||||||
} else {
|
} else {
|
||||||
val skipLabel = createLabelName()
|
val skipLabel = createLabelName()
|
||||||
val values = choice.values.children.map {it as PtNumber}
|
val values = choice.values.children.map {it as PtNumber}
|
||||||
|
val choiceReg = registers.nextFree()
|
||||||
if(values.size==1) {
|
if(values.size==1) {
|
||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
||||||
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
|
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, reg2=choiceReg, labelSymbol = skipLabel)
|
||||||
result += chunk
|
result += chunk
|
||||||
result += translateNode(choice.statements)
|
result += translateNode(choice.statements)
|
||||||
if(choice.statements.children.last() !is PtReturn)
|
if(choice.statements.children.last() !is PtReturn)
|
||||||
@ -414,7 +428,7 @@ class IRCodeGen(
|
|||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
for (value in values) {
|
for (value in values) {
|
||||||
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
||||||
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
|
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, reg2=choiceReg, labelSymbol = matchLabel)
|
||||||
}
|
}
|
||||||
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||||
result += chunk
|
result += chunk
|
||||||
@ -504,8 +518,6 @@ class IRCodeGen(
|
|||||||
val step = iterable.step.number.toInt()
|
val step = iterable.step.number.toInt()
|
||||||
if (step==0)
|
if (step==0)
|
||||||
throw AssemblyError("step 0")
|
throw AssemblyError("step 0")
|
||||||
val indexReg = registers.nextFree()
|
|
||||||
val endvalueReg = registers.nextFree()
|
|
||||||
require(forLoop.variable.name == loopvar.scopedName)
|
require(forLoop.variable.name == loopvar.scopedName)
|
||||||
val loopvarSymbol = forLoop.variable.name
|
val loopvarSymbol = forLoop.variable.name
|
||||||
val loopvarDt = when(loopvar) {
|
val loopvarDt = when(loopvar) {
|
||||||
@ -517,20 +529,22 @@ class IRCodeGen(
|
|||||||
val loopLabel = createLabelName()
|
val loopLabel = createLabelName()
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
result += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
|
val toTr = expressionEval.translateExpression(iterable.to)
|
||||||
result += expressionEval.translateExpression(iterable.from, indexReg, -1)
|
addToResult(result, toTr, toTr.resultReg, -1)
|
||||||
|
val fromTr = expressionEval.translateExpression(iterable.from)
|
||||||
|
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||||
|
|
||||||
val labelAfterFor = createLabelName()
|
val labelAfterFor = createLabelName()
|
||||||
val greaterOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGTS else Opcode.BGT
|
val greaterOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGTS else Opcode.BGT
|
||||||
addInstr(result, IRInstruction(greaterOpcode, loopvarDtIr, indexReg, endvalueReg, labelSymbol=labelAfterFor), null)
|
addInstr(result, IRInstruction(greaterOpcode, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor), null)
|
||||||
|
|
||||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol), null)
|
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol), null)
|
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||||
// if endvalue >= index, iterate loop
|
// if endvalue >= index, iterate loop
|
||||||
val branchOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGES else Opcode.BGE
|
val branchOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGES else Opcode.BGE
|
||||||
addInstr(result, IRInstruction(branchOpcode, loopvarDtIr, reg1=endvalueReg, reg2=indexReg, labelSymbol=loopLabel), null)
|
addInstr(result, IRInstruction(branchOpcode, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel), null)
|
||||||
|
|
||||||
result += IRCodeChunk(labelAfterFor, null)
|
result += IRCodeChunk(labelAfterFor, null)
|
||||||
return result
|
return result
|
||||||
@ -883,173 +897,421 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(ifElse: PtIfElse): IRCodeChunks {
|
private fun translate(ifElse: PtIfElse): IRCodeChunks {
|
||||||
if(ifElse.condition.operator !in ComparisonOperators)
|
val condition = ifElse.condition
|
||||||
|
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
||||||
|
when (condition) {
|
||||||
|
is PtBinaryExpression -> {
|
||||||
|
require(!options.useNewExprCode)
|
||||||
|
if(condition.operator !in ComparisonOperators)
|
||||||
throw AssemblyError("if condition should only be a binary comparison expression")
|
throw AssemblyError("if condition should only be a binary comparison expression")
|
||||||
|
|
||||||
val signed = ifElse.condition.left.type in SignedDatatypes
|
val signed = condition.left.type in SignedDatatypes
|
||||||
val irDt = irType(ifElse.condition.left.type)
|
val irDtLeft = irType(condition.left.type)
|
||||||
|
return when {
|
||||||
|
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
|
||||||
|
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
|
||||||
|
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// if X --> meaning: if X!=0
|
||||||
|
require(options.useNewExprCode)
|
||||||
|
val irDt = irType(condition.type)
|
||||||
|
val signed = condition.type in SignedDatatypes
|
||||||
|
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
||||||
|
translateIfFollowedByJustGoto(ifElse, goto, irDt, signed)
|
||||||
|
} else {
|
||||||
|
translateIfElseNonZeroComparison(ifElse, irDt, signed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
|
||||||
if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
|
||||||
// special case the form: if <condition> goto <place>
|
val conditionBinExpr = ifElse.condition as? PtBinaryExpression
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val leftRegNum = registers.nextFree()
|
if(conditionBinExpr==null) {
|
||||||
val rightRegNum = registers.nextFree()
|
if(irDtLeft==IRDataType.FLOAT)
|
||||||
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
|
throw AssemblyError("condition value should not be float")
|
||||||
result += expressionEval.translateExpression(ifElse.condition.right, rightRegNum, -1)
|
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
if (irDtLeft == IRDataType.FLOAT) {
|
||||||
|
val leftTr = expressionEval.translateExpression(conditionBinExpr.left)
|
||||||
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
|
val rightTr = expressionEval.translateExpression(conditionBinExpr.right)
|
||||||
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
val compResultReg = registers.nextFree()
|
||||||
|
it += IRInstruction(
|
||||||
|
Opcode.FCOMP,
|
||||||
|
IRDataType.FLOAT,
|
||||||
|
reg1 = compResultReg,
|
||||||
|
fpReg1 = leftTr.resultFpReg,
|
||||||
|
fpReg2 = rightTr.resultFpReg
|
||||||
|
)
|
||||||
|
val gotoOpcode = when (conditionBinExpr.operator) {
|
||||||
|
"==" -> Opcode.BZ
|
||||||
|
"!=" -> Opcode.BNZ
|
||||||
|
"<" -> Opcode.BLEZS
|
||||||
|
">" -> Opcode.BGEZS
|
||||||
|
"<=" -> Opcode.BLZS
|
||||||
|
">=" -> Opcode.BGZS
|
||||||
|
else -> throw AssemblyError("weird operator")
|
||||||
|
}
|
||||||
|
it += if (goto.address != null)
|
||||||
|
IRInstruction(
|
||||||
|
gotoOpcode,
|
||||||
|
IRDataType.BYTE,
|
||||||
|
reg1 = compResultReg,
|
||||||
|
value = goto.address?.toInt()
|
||||||
|
)
|
||||||
|
else if (goto.generatedLabel != null)
|
||||||
|
IRInstruction(
|
||||||
|
gotoOpcode,
|
||||||
|
IRDataType.BYTE,
|
||||||
|
reg1 = compResultReg,
|
||||||
|
labelSymbol = goto.generatedLabel
|
||||||
|
)
|
||||||
|
else
|
||||||
|
IRInstruction(
|
||||||
|
gotoOpcode,
|
||||||
|
IRDataType.BYTE,
|
||||||
|
reg1 = compResultReg,
|
||||||
|
labelSymbol = goto.identifier!!.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
val rightConst = conditionBinExpr.right.asConstInteger()
|
||||||
|
if (rightConst == 0)
|
||||||
|
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||||
|
else {
|
||||||
|
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ifZeroIntThenJump(
|
||||||
|
result: MutableList<IRCodeChunkBase>,
|
||||||
|
ifElse: PtIfElse,
|
||||||
|
signed: Boolean,
|
||||||
|
irDtLeft: IRDataType,
|
||||||
|
goto: PtJump
|
||||||
|
) {
|
||||||
|
val condition = ifElse.condition as PtBinaryExpression
|
||||||
|
val leftTr = expressionEval.translateExpression(condition.left)
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val opcode = when (condition.operator) {
|
||||||
|
"==" -> Opcode.BZ
|
||||||
|
"!=" -> Opcode.BNZ
|
||||||
|
"<" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||||
|
">" -> if (signed) Opcode.BGZS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||||
|
"<=" -> if (signed) Opcode.BLEZS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||||
|
">=" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||||
|
else -> throw AssemblyError("invalid comparison operator")
|
||||||
|
}
|
||||||
|
if (goto.address != null)
|
||||||
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, value = goto.address?.toInt()), null)
|
||||||
|
else if (goto.generatedLabel != null)
|
||||||
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.generatedLabel), null)
|
||||||
|
else
|
||||||
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.identifier!!.name), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ifNonZeroIntThenJump(
|
||||||
|
result: MutableList<IRCodeChunkBase>,
|
||||||
|
ifElse: PtIfElse,
|
||||||
|
signed: Boolean,
|
||||||
|
irDtLeft: IRDataType,
|
||||||
|
goto: PtJump
|
||||||
|
) {
|
||||||
|
val conditionBinExpr = ifElse.condition as? PtBinaryExpression
|
||||||
|
if(conditionBinExpr==null) {
|
||||||
|
val tr = expressionEval.translateExpression(ifElse.condition)
|
||||||
|
result += tr.chunks
|
||||||
|
if (goto.address != null)
|
||||||
|
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, value = goto.address?.toInt()), null)
|
||||||
|
else if (goto.generatedLabel != null)
|
||||||
|
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, labelSymbol = goto.generatedLabel), null)
|
||||||
|
else
|
||||||
|
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, labelSymbol = goto.identifier!!.name), null)
|
||||||
|
} else {
|
||||||
|
val leftTr = expressionEval.translateExpression(conditionBinExpr.left)
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = expressionEval.translateExpression(conditionBinExpr.right)
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val opcode: Opcode
|
val opcode: Opcode
|
||||||
val firstReg: Int
|
val firstReg: Int
|
||||||
val secondReg: Int
|
val secondReg: Int
|
||||||
when(ifElse.condition.operator) {
|
when (conditionBinExpr.operator) {
|
||||||
"==" -> {
|
"==" -> {
|
||||||
opcode = Opcode.BEQ
|
opcode = Opcode.BEQ
|
||||||
firstReg = leftRegNum
|
firstReg = leftTr.resultReg
|
||||||
secondReg = rightRegNum
|
secondReg = rightTr.resultReg
|
||||||
}
|
}
|
||||||
"!=" -> {
|
"!=" -> {
|
||||||
opcode = Opcode.BNE
|
opcode = Opcode.BNE
|
||||||
firstReg = leftRegNum
|
firstReg = leftTr.resultReg
|
||||||
secondReg = rightRegNum
|
secondReg = rightTr.resultReg
|
||||||
}
|
}
|
||||||
"<" -> {
|
"<" -> {
|
||||||
// swapped '>'
|
// swapped '>'
|
||||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||||
firstReg = rightRegNum
|
firstReg = rightTr.resultReg
|
||||||
secondReg = leftRegNum
|
secondReg = leftTr.resultReg
|
||||||
}
|
}
|
||||||
">" -> {
|
">" -> {
|
||||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||||
firstReg = leftRegNum
|
firstReg = leftTr.resultReg
|
||||||
secondReg = rightRegNum
|
secondReg = rightTr.resultReg
|
||||||
}
|
}
|
||||||
"<=" -> {
|
"<=" -> {
|
||||||
// swapped '>='
|
// swapped '>='
|
||||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||||
firstReg = rightRegNum
|
firstReg = rightTr.resultReg
|
||||||
secondReg = leftRegNum
|
secondReg = leftTr.resultReg
|
||||||
}
|
}
|
||||||
">=" -> {
|
">=" -> {
|
||||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||||
firstReg = leftRegNum
|
firstReg = leftTr.resultReg
|
||||||
secondReg = rightRegNum
|
secondReg = rightTr.resultReg
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid comparison operator")
|
else -> throw AssemblyError("invalid comparison operator")
|
||||||
}
|
}
|
||||||
if (goto.address != null)
|
if (goto.address != null)
|
||||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, value = goto.address?.toInt()), null)
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
|
||||||
else if (goto.generatedLabel != null)
|
else if (goto.generatedLabel != null)
|
||||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.generatedLabel), null)
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||||
else
|
else
|
||||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.identifier!!.name), null)
|
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||||
return result
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateNonZeroComparison(): IRCodeChunks {
|
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val leftRegNum = registers.nextFree()
|
val elseBranch: Opcode
|
||||||
val rightRegNum = registers.nextFree()
|
val compResultReg: Int
|
||||||
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
|
val branchDt: IRDataType
|
||||||
result += expressionEval.translateExpression(ifElse.condition.right, rightRegNum, -1)
|
val condition = ifElse.condition as PtBinaryExpression
|
||||||
|
if(irDtLeft==IRDataType.FLOAT) {
|
||||||
val elseBranchOpcode: Opcode
|
branchDt = IRDataType.BYTE
|
||||||
val elseBranchFirstReg: Int
|
compResultReg = registers.nextFree()
|
||||||
val elseBranchSecondReg: Int
|
val leftTr = expressionEval.translateExpression(condition.left)
|
||||||
when(ifElse.condition.operator) {
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
"==" -> {
|
result += IRCodeChunk(null, null).also {
|
||||||
elseBranchOpcode = Opcode.BNE
|
val rightFpReg = registers.nextFreeFloat()
|
||||||
elseBranchFirstReg = leftRegNum
|
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, fpValue = 0f)
|
||||||
elseBranchSecondReg = rightRegNum
|
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
|
||||||
}
|
}
|
||||||
"!=" -> {
|
elseBranch = when (condition.operator) {
|
||||||
elseBranchOpcode = Opcode.BEQ
|
"==" -> Opcode.BNZ
|
||||||
elseBranchFirstReg = leftRegNum
|
"!=" -> Opcode.BZ
|
||||||
elseBranchSecondReg = rightRegNum
|
"<" -> Opcode.BGEZS
|
||||||
|
">" -> Opcode.BLEZS
|
||||||
|
"<=" -> Opcode.BGZS
|
||||||
|
">=" -> Opcode.BLZS
|
||||||
|
else -> throw AssemblyError("weird operator")
|
||||||
}
|
}
|
||||||
"<" -> {
|
|
||||||
// else part when left >= right
|
|
||||||
elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE
|
|
||||||
elseBranchFirstReg = leftRegNum
|
|
||||||
elseBranchSecondReg = rightRegNum
|
|
||||||
}
|
|
||||||
">" -> {
|
|
||||||
// else part when left <= right --> right >= left
|
|
||||||
elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE
|
|
||||||
elseBranchFirstReg = rightRegNum
|
|
||||||
elseBranchSecondReg = leftRegNum
|
|
||||||
}
|
|
||||||
"<=" -> {
|
|
||||||
// else part when left > right
|
|
||||||
elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT
|
|
||||||
elseBranchFirstReg = leftRegNum
|
|
||||||
elseBranchSecondReg = rightRegNum
|
|
||||||
}
|
|
||||||
">=" -> {
|
|
||||||
// else part when left < right --> right > left
|
|
||||||
elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT
|
|
||||||
elseBranchFirstReg = rightRegNum
|
|
||||||
elseBranchSecondReg = leftRegNum
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid comparison operator")
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
|
||||||
// if and else parts
|
|
||||||
val elseLabel = createLabelName()
|
|
||||||
val afterIfLabel = createLabelName()
|
|
||||||
addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = elseLabel), null)
|
|
||||||
result += translateNode(ifElse.ifScope)
|
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
|
||||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
|
||||||
result += IRCodeChunk(afterIfLabel, null)
|
|
||||||
} else {
|
} else {
|
||||||
// only if part
|
// integer comparisons
|
||||||
val afterIfLabel = createLabelName()
|
branchDt = irDtLeft
|
||||||
addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = afterIfLabel), null)
|
val tr = expressionEval.translateExpression(condition.left)
|
||||||
result += translateNode(ifElse.ifScope)
|
compResultReg = tr.resultReg
|
||||||
result += IRCodeChunk(afterIfLabel, null)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
}
|
elseBranch = when (condition.operator) {
|
||||||
return result
|
"==" -> Opcode.BNZ
|
||||||
}
|
"!=" -> Opcode.BZ
|
||||||
|
"<" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||||
fun translateZeroComparison(): IRCodeChunks {
|
">" -> if (signed) Opcode.BLEZS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||||
fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks {
|
"<=" -> if (signed) Opcode.BGZS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
">=" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||||
val leftReg = registers.nextFree()
|
|
||||||
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
|
|
||||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
|
||||||
// if and else parts
|
|
||||||
val elseLabel = createLabelName()
|
|
||||||
val afterIfLabel = createLabelName()
|
|
||||||
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = elseLabel), null)
|
|
||||||
result += translateNode(ifElse.ifScope)
|
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
|
||||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
|
||||||
result += IRCodeChunk(afterIfLabel, null)
|
|
||||||
} else {
|
|
||||||
// only if part
|
|
||||||
val afterIfLabel = createLabelName()
|
|
||||||
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel), null)
|
|
||||||
result += translateNode(ifElse.ifScope)
|
|
||||||
result += IRCodeChunk(afterIfLabel, null)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (ifElse.condition.operator) {
|
|
||||||
"==" -> equalOrNotEqualZero(Opcode.BNZ)
|
|
||||||
"!=" -> equalOrNotEqualZero(Opcode.BZ)
|
|
||||||
"<" -> if(signed) equalOrNotEqualZero(Opcode.BGEZS) else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
|
||||||
">" -> if(signed) equalOrNotEqualZero(Opcode.BLEZS) else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
|
||||||
"<=" -> if(signed) equalOrNotEqualZero(Opcode.BGZS) else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
|
||||||
">=" -> if(signed) equalOrNotEqualZero(Opcode.BLZS) else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
|
||||||
else -> throw AssemblyError("weird operator")
|
else -> throw AssemblyError("weird operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if(constValue(ifElse.condition.right)==0.0)
|
if(ifElse.elseScope.children.isEmpty()) {
|
||||||
translateZeroComparison()
|
// just if
|
||||||
else
|
val afterIfLabel = createLabelName()
|
||||||
translateNonZeroComparison()
|
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, labelSymbol = afterIfLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
} else {
|
||||||
|
// if and else
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, labelSymbol = elseLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||||
|
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val elseBranchOpcode: Opcode
|
||||||
|
val elseBranchFirstReg: Int
|
||||||
|
val elseBranchSecondReg: Int
|
||||||
|
val branchDt: IRDataType
|
||||||
|
val condition = ifElse.condition as? PtBinaryExpression
|
||||||
|
if(condition==null) {
|
||||||
|
if(irDtLeft==IRDataType.FLOAT)
|
||||||
|
throw AssemblyError("condition value should not be float")
|
||||||
|
val tr = expressionEval.translateExpression(ifElse.condition)
|
||||||
|
result += tr.chunks
|
||||||
|
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(result, IRInstruction(Opcode.BZ, irDtLeft, reg1=tr.resultReg, labelSymbol = elseLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||||
|
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
} else {
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(result, IRInstruction(Opcode.BZ, irDtLeft, reg1=tr.resultReg, labelSymbol = afterIfLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
if (irDtLeft == IRDataType.FLOAT) {
|
||||||
|
val leftTr = expressionEval.translateExpression(condition.left)
|
||||||
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
|
val rightTr = expressionEval.translateExpression(condition.right)
|
||||||
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
|
val compResultReg = registers.nextFree()
|
||||||
|
addInstr(
|
||||||
|
result,
|
||||||
|
IRInstruction(
|
||||||
|
Opcode.FCOMP,
|
||||||
|
IRDataType.FLOAT,
|
||||||
|
reg1 = compResultReg,
|
||||||
|
fpReg1 = leftTr.resultFpReg,
|
||||||
|
fpReg2 = rightTr.resultFpReg
|
||||||
|
),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
val elseBranch = when (condition.operator) {
|
||||||
|
"==" -> Opcode.BNZ
|
||||||
|
"!=" -> Opcode.BZ
|
||||||
|
"<" -> Opcode.BGEZS
|
||||||
|
">" -> Opcode.BLEZS
|
||||||
|
"<=" -> Opcode.BGZS
|
||||||
|
">=" -> Opcode.BLZS
|
||||||
|
else -> throw AssemblyError("weird operator")
|
||||||
|
}
|
||||||
|
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||||
|
// if and else parts
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(
|
||||||
|
result,
|
||||||
|
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = elseLabel),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||||
|
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
} else {
|
||||||
|
// only if part
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(
|
||||||
|
result,
|
||||||
|
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = afterIfLabel),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// integer comparisons
|
||||||
|
branchDt = irDtLeft
|
||||||
|
val leftTr = expressionEval.translateExpression(condition.left)
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = expressionEval.translateExpression(condition.right)
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
|
when (condition.operator) {
|
||||||
|
"==" -> {
|
||||||
|
elseBranchOpcode = Opcode.BNE
|
||||||
|
elseBranchFirstReg = leftTr.resultReg
|
||||||
|
elseBranchSecondReg = rightTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
"!=" -> {
|
||||||
|
elseBranchOpcode = Opcode.BEQ
|
||||||
|
elseBranchFirstReg = leftTr.resultReg
|
||||||
|
elseBranchSecondReg = rightTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
"<" -> {
|
||||||
|
// else part when left >= right
|
||||||
|
elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||||
|
elseBranchFirstReg = leftTr.resultReg
|
||||||
|
elseBranchSecondReg = rightTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
">" -> {
|
||||||
|
// else part when left <= right --> right >= left
|
||||||
|
elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||||
|
elseBranchFirstReg = rightTr.resultReg
|
||||||
|
elseBranchSecondReg = leftTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
"<=" -> {
|
||||||
|
// else part when left > right
|
||||||
|
elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||||
|
elseBranchFirstReg = leftTr.resultReg
|
||||||
|
elseBranchSecondReg = rightTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
">=" -> {
|
||||||
|
// else part when left < right --> right > left
|
||||||
|
elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||||
|
elseBranchFirstReg = rightTr.resultReg
|
||||||
|
elseBranchSecondReg = leftTr.resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw AssemblyError("invalid comparison operator")
|
||||||
|
}
|
||||||
|
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||||
|
// if and else parts
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(
|
||||||
|
result, IRInstruction(
|
||||||
|
elseBranchOpcode, branchDt,
|
||||||
|
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||||
|
labelSymbol = elseLabel
|
||||||
|
), null
|
||||||
|
)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||||
|
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
} else {
|
||||||
|
// only if part
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(
|
||||||
|
result, IRInstruction(
|
||||||
|
elseBranchOpcode, branchDt,
|
||||||
|
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||||
|
labelSymbol = afterIfLabel
|
||||||
|
), null
|
||||||
|
)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
|
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
|
||||||
@ -1078,13 +1340,13 @@ class IRCodeGen(
|
|||||||
val address = (memory.address as PtNumber).number.toInt()
|
val address = (memory.address as PtNumber).number.toInt()
|
||||||
addInstr(result, IRInstruction(operationMem, irDt, value = address), null)
|
addInstr(result, IRInstruction(operationMem, irDt, value = address), null)
|
||||||
} else {
|
} else {
|
||||||
val incReg = registers.nextFree()
|
val tr = expressionEval.translateExpression(memory.address)
|
||||||
val addressReg = registers.nextFree()
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
|
||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = addressReg)
|
val incReg = registers.nextFree()
|
||||||
|
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||||
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||||
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = addressReg)
|
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||||
result += chunk
|
result += chunk
|
||||||
}
|
}
|
||||||
} else if (array!=null) {
|
} else if (array!=null) {
|
||||||
@ -1095,13 +1357,13 @@ class IRCodeGen(
|
|||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
|
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
|
||||||
} else {
|
} else {
|
||||||
val incReg = registers.nextFree()
|
val indexTr = expressionEval.translateExpression(array.index)
|
||||||
val indexReg = registers.nextFree()
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
result += expressionEval.translateExpression(array.index, indexReg, -1)
|
|
||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
val incReg = registers.nextFree()
|
||||||
|
chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||||
chunk += IRInstruction(operationRegister, irDt, reg1=incReg)
|
chunk += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||||
chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||||
result += chunk
|
result += chunk
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -1122,15 +1384,15 @@ class IRCodeGen(
|
|||||||
|
|
||||||
val repeatLabel = createLabelName()
|
val repeatLabel = createLabelName()
|
||||||
val skipRepeatLabel = createLabelName()
|
val skipRepeatLabel = createLabelName()
|
||||||
val counterReg = registers.nextFree()
|
|
||||||
val irDt = irType(repeat.count.type)
|
val irDt = irType(repeat.count.type)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += expressionEval.translateExpression(repeat.count, counterReg, -1)
|
val countTr = expressionEval.translateExpression(repeat.count)
|
||||||
addInstr(result, IRInstruction(Opcode.BZ, irDt, reg1=counterReg, labelSymbol = skipRepeatLabel), null)
|
addToResult(result, countTr, countTr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.BZ, irDt, reg1=countTr.resultReg, labelSymbol = skipRepeatLabel), null)
|
||||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
chunk += IRInstruction(Opcode.DEC, irDt, reg1=counterReg)
|
chunk += IRInstruction(Opcode.DEC, irDt, reg1=countTr.resultReg)
|
||||||
chunk += IRInstruction(Opcode.BNZ, irDt, reg1=counterReg, labelSymbol = repeatLabel)
|
chunk += IRInstruction(Opcode.BNZ, irDt, reg1=countTr.resultReg, labelSymbol = repeatLabel)
|
||||||
result += chunk
|
result += chunk
|
||||||
result += IRCodeChunk(skipRepeatLabel, null)
|
result += IRCodeChunk(skipRepeatLabel, null)
|
||||||
return result
|
return result
|
||||||
@ -1161,14 +1423,20 @@ class IRCodeGen(
|
|||||||
private fun translate(ret: PtReturn): IRCodeChunks {
|
private fun translate(ret: PtReturn): IRCodeChunks {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val value = ret.value
|
val value = ret.value
|
||||||
if(value!=null) {
|
if(value==null) {
|
||||||
// Call Convention: return value is always returned in r0 (or fr0 if float)
|
|
||||||
result += if(value.type==DataType.FLOAT)
|
|
||||||
expressionEval.translateExpression(value, -1, 0)
|
|
||||||
else
|
|
||||||
expressionEval.translateExpression(value, 0, -1)
|
|
||||||
}
|
|
||||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||||
|
} else {
|
||||||
|
if(value.type==DataType.FLOAT) {
|
||||||
|
val tr = expressionEval.translateExpression(value)
|
||||||
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
|
addInstr(result, IRInstruction(Opcode.RETURNREG, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val tr = expressionEval.translateExpression(value)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.RETURNREG, irType(value.type) , reg1=tr.resultReg), null)
|
||||||
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,10 +1527,28 @@ class IRCodeGen(
|
|||||||
return "prog8_label_gen_$labelSequenceNumber"
|
return "prog8_label_gen_$labelSequenceNumber"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks =
|
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||||
builtinFuncGen.translate(call, resultRegister)
|
= builtinFuncGen.translate(call)
|
||||||
|
|
||||||
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
|
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
|
||||||
|
|
||||||
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
||||||
|
|
||||||
|
fun getReusableTempvar(scope: PtNamedNode, type: DataType): PtIdentifier {
|
||||||
|
val uniqueId = Pair(scope, type).hashCode().toUInt()
|
||||||
|
val tempvarname = "${scope.scopedName}.tempvar_${uniqueId}"
|
||||||
|
val tempvar = PtIdentifier(tempvarname, type, Position.DUMMY)
|
||||||
|
val staticVar = StStaticVariable(
|
||||||
|
tempvarname,
|
||||||
|
type,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ZeropageWish.DONTCARE,
|
||||||
|
tempvar
|
||||||
|
)
|
||||||
|
irSymbolTable.add(staticVar)
|
||||||
|
return tempvar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
if(ins.opcode== Opcode.PUSH) {
|
if(ins.opcode== Opcode.PUSH) {
|
||||||
if(idx < chunk.instructions.size-1) {
|
if(idx < chunk.instructions.size-1) {
|
||||||
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
val insAfter = chunk.instructions[idx+1]
|
||||||
if(insAfter!=null && insAfter.opcode == Opcode.POP) {
|
if(insAfter.opcode == Opcode.POP) {
|
||||||
if(ins.reg1==insAfter.reg1) {
|
if(ins.reg1==insAfter.reg1) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
@ -143,16 +143,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
||||||
if(idx < chunk.instructions.size-1) {
|
if(idx < chunk.instructions.size-1) {
|
||||||
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
val insAfter = chunk.instructions[idx+1]
|
||||||
if(insAfter?.opcode == ins.opcode) {
|
if(insAfter.opcode == ins.opcode) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
|
else if(ins.opcode== Opcode.SEC && insAfter.opcode== Opcode.CLC) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
|
else if(ins.opcode== Opcode.CLC && insAfter.opcode== Opcode.SEC) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
@ -174,10 +174,30 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove useless RETURN
|
// remove useless RETURN
|
||||||
if(ins.opcode == Opcode.RETURN && idx>0) {
|
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNREG)) {
|
||||||
val previous = chunk.instructions[idx-1] as? IRInstruction
|
val previous = chunk.instructions[idx-1]
|
||||||
if(previous?.opcode in OpcodesThatJump) {
|
if(previous.opcode in OpcodesThatJump) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace subsequent opcodes that jump by just the first
|
||||||
|
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
|
||||||
|
val previous = chunk.instructions[idx-1]
|
||||||
|
if(previous.opcode in OpcodesThatJump) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace call + return --> jump
|
||||||
|
if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||||
|
val previous = chunk.instructions[idx-1]
|
||||||
|
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLRVAL) {
|
||||||
|
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, value=previous.value, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,9 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
|||||||
irprog.blocks.forEach { block ->
|
irprog.blocks.forEach { block ->
|
||||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||||
if(sub.isEmpty()) {
|
if(sub.isEmpty()) {
|
||||||
if(!sub.position.file.startsWith(libraryFilePrefix))
|
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||||
|
}
|
||||||
block.children.remove(sub)
|
block.children.remove(sub)
|
||||||
numRemoved++
|
numRemoved++
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import prog8.intermediate.SyscallRegisterBase
|
|||||||
|
|
||||||
internal class RegisterPool {
|
internal class RegisterPool {
|
||||||
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
||||||
|
// TODO set this back to 0 once 'resultRegister' has been removed everywhere and SYSCALL/DIVMOD fixed?
|
||||||
private var firstFree: Int=3
|
private var firstFree: Int=3
|
||||||
private var firstFreeFloat: Int=3
|
private var firstFreeFloat: Int=3
|
||||||
|
|
||||||
|
@ -2,10 +2,7 @@ package prog8.codegen.vm
|
|||||||
|
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.code.core.ICodeGeneratorBackend
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
import prog8.intermediate.IRProgram
|
import prog8.intermediate.IRProgram
|
||||||
@ -26,7 +23,7 @@ class VmCodeGen: ICodeGeneratorBackend {
|
|||||||
|
|
||||||
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
// the VM reads the IR file from disk.
|
// the VM reads the IR file from disk.
|
||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
return true
|
return true
|
||||||
|
@ -32,11 +32,15 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
val warnings = mutableListOf<String>()
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
override fun err(msg: String, position: Position) {
|
override fun err(msg: String, position: Position) {
|
||||||
errors.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in errors)
|
||||||
|
errors.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun warn(msg: String, position: Position) {
|
override fun warn(msg: String, position: Position) {
|
||||||
warnings.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in warnings)
|
||||||
|
warnings.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
@ -100,4 +100,343 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
irChunks.size shouldBeGreaterThan 4
|
irChunks.size shouldBeGreaterThan 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("float comparison expressions against zero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==0
|
||||||
|
// nop
|
||||||
|
// if f1!=0
|
||||||
|
// nop
|
||||||
|
// if f1>0
|
||||||
|
// nop
|
||||||
|
// if f1<0
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("float comparison expressions against nonzero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==42
|
||||||
|
// nop
|
||||||
|
// if f1!=42
|
||||||
|
// nop
|
||||||
|
// if f1>42
|
||||||
|
// nop
|
||||||
|
// if f1<42
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("float conditional jump") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==42
|
||||||
|
// goto $c000
|
||||||
|
// if f1>42
|
||||||
|
// goto $c000
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup())
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup())
|
||||||
|
sub.add(if2)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer comparison expressions against zero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// byte @shared sb1
|
||||||
|
//
|
||||||
|
// if sb1==0
|
||||||
|
// nop
|
||||||
|
// if sb1!=0
|
||||||
|
// nop
|
||||||
|
// if sb1>0
|
||||||
|
// nop
|
||||||
|
// if sb1<0
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer comparison expressions against nonzero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// byte @shared sb1
|
||||||
|
//
|
||||||
|
// if sb1==42
|
||||||
|
// nop
|
||||||
|
// if sb1!=42
|
||||||
|
// nop
|
||||||
|
// if sb1>42
|
||||||
|
// nop
|
||||||
|
// if sb1<42
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer conditional jump") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// ubyte @shared ub1
|
||||||
|
//
|
||||||
|
// if ub1==42
|
||||||
|
// goto $c000
|
||||||
|
// if ub1>42
|
||||||
|
// goto $c000
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup())
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup())
|
||||||
|
sub.add(if2)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
@ -87,8 +87,27 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
if(forloop!=null && identifier===forloop.loopVar)
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
|
val dt = identifier.inferType(program)
|
||||||
|
if(!dt.isKnown || !dt.isNumeric)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val cval = identifier.constValue(program) ?: return noModifications
|
val cval = identifier.constValue(program) ?: return noModifications
|
||||||
|
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||||
|
if(arrayIdx!=null && cval.type in NumericDatatypes) {
|
||||||
|
// special case when the identifier is used as a pointer var
|
||||||
|
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||||
|
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||||
|
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, identifier.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||||
|
} else {
|
||||||
|
val memread = DirectMemoryRead(add, identifier.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
return when (cval.type) {
|
return when (cval.type) {
|
||||||
in NumericDatatypes -> listOf(
|
in NumericDatatypes -> listOf(
|
||||||
IAstModification.ReplaceNode(
|
IAstModification.ReplaceNode(
|
||||||
@ -223,6 +242,17 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.ARRAY_BOOL -> {
|
||||||
|
val numericLv = decl.value as? NumericLiteral
|
||||||
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
|
if(numericLv!=null) {
|
||||||
|
// arraysize initializer is a single int, and we know the size.
|
||||||
|
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0
|
||||||
|
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>()
|
||||||
|
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nothing to do for this type
|
// nothing to do for this type
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,7 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
return if(sub.isAsmSubroutine) {
|
return if(sub.isAsmSubroutine) {
|
||||||
// simply insert the asm for the argument-less routine
|
// simply insert the asm for the argument-less routine
|
||||||
|
sub.hasBeenInlined=true
|
||||||
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
||||||
} else {
|
} else {
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
@ -192,12 +193,16 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
val fcall = toInline.value as? FunctionCallExpression
|
val fcall = toInline.value as? FunctionCallExpression
|
||||||
if(fcall!=null) {
|
if(fcall!=null) {
|
||||||
// insert the function call expression as a void function call directly
|
// insert the function call expression as a void function call directly
|
||||||
|
sub.hasBeenInlined=true
|
||||||
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||||
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||||
} else
|
} else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
else -> {
|
||||||
|
sub.hasBeenInlined=true
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,8 +231,10 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
is Return -> {
|
is Return -> {
|
||||||
// is an expression, so we have to have a Return here in the inlined sub
|
// is an expression, so we have to have a Return here in the inlined sub
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
if(toInline.value!=null)
|
if(toInline.value!=null) {
|
||||||
|
sub.hasBeenInlined=true
|
||||||
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
||||||
|
}
|
||||||
else
|
else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,16 @@ class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val iterationCount = forLoop.constIterationCount(program)
|
||||||
|
if(iterationCount!=null) {
|
||||||
|
val loopName = forLoop.loopVar.nameInSource
|
||||||
|
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) {
|
||||||
|
errors.warn("for loop can be replaced with repeat loop, possibly also remove the loop variable", forLoop.position)
|
||||||
|
val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,13 +326,13 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0 && compTarget.name!=VMTarget.NAME) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
|
||||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||||
}
|
}
|
||||||
listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,7 +340,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0 && compTarget.name!=VMTarget.NAME) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
|
||||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
@ -374,4 +384,11 @@ class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(unrollLoop.iterations<1)
|
||||||
|
listOf(IAstModification.Remove(unrollLoop, parent as IStatementContainer))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,13 +64,17 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars) {
|
if (block.containsNoCodeNorVars) {
|
||||||
if(block.name != internedStringsModuleName)
|
if(block.name != internedStringsModuleName) {
|
||||||
|
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
}
|
||||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
if(callgraph.unused(block)) {
|
if(callgraph.unused(block)) {
|
||||||
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR})
|
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) {
|
||||||
|
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
}
|
||||||
program.removeInternedStringsFromRemovedBlock(block)
|
program.removeInternedStringsFromRemovedBlock(block)
|
||||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
@ -81,7 +85,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
||||||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
|
||||||
if(callgraph.unused(subroutine)) {
|
if(callgraph.unused(subroutine)) {
|
||||||
if(subroutine.containsNoCodeNorVars) {
|
if(subroutine.containsNoCodeNorVars) {
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary)
|
||||||
@ -93,9 +97,12 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
return removals
|
return removals
|
||||||
}
|
}
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary && !subroutine.hasBeenInlined) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
}
|
||||||
|
if(!subroutine.inline) {
|
||||||
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
||||||
|
}
|
||||||
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +117,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
||||||
val usages = callgraph.usages(decl)
|
val usages = callgraph.usages(decl)
|
||||||
if (usages.isEmpty()) {
|
if (usages.isEmpty()) {
|
||||||
// if(!decl.definingModule.isLibrary)
|
if(!decl.definingModule.isLibrary)
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
@ -118,8 +125,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if(usages.size==1) {
|
if(usages.size==1) {
|
||||||
val singleUse = usages[0].parent
|
val singleUse = usages[0].parent
|
||||||
if(singleUse is AssignTarget) {
|
if(singleUse is AssignTarget) {
|
||||||
val assignment = singleUse.parent as Assignment
|
val assignment = singleUse.parent as? Assignment
|
||||||
if(assignment.origin==AssignmentOrigin.VARINIT) {
|
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
|
||||||
if(assignment.value.isSimple) {
|
if(assignment.value.isSimple) {
|
||||||
// remove the vardecl
|
// remove the vardecl
|
||||||
if(!decl.definingModule.isLibrary)
|
if(!decl.definingModule.isLibrary)
|
||||||
|
@ -34,14 +34,14 @@ dependencies {
|
|||||||
implementation project(':codeGenIntermediate')
|
implementation project(':codeGenIntermediate')
|
||||||
implementation project(':codeGenExperimental')
|
implementation project(':codeGenExperimental')
|
||||||
implementation project(':virtualmachine')
|
implementation project(':virtualmachine')
|
||||||
implementation 'org.antlr:antlr4-runtime:4.11.1'
|
implementation "org.antlr:antlr4-runtime:4.12.0"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
testImplementation project(':intermediate')
|
testImplementation project(':intermediate')
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
|
@ -251,7 +251,7 @@ pop_float_fac1 .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
copy_float .proc
|
copy_float .proc
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_W1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
sta _target+1
|
sta _target+1
|
||||||
sty _target+2
|
sty _target+2
|
||||||
|
@ -39,8 +39,8 @@ romsub $af18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt valu
|
|||||||
romsub $af1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2 NOTE: use FADDT2() instead!
|
romsub $af1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2 NOTE: use FADDT2() instead!
|
||||||
romsub $af1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
romsub $af1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
romsub $af21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2 NOTE: use FMULTT2() instead!
|
romsub $af21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2 NOTE: use FMULTT2() instead!
|
||||||
romsub $af24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $af24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||||
romsub $af27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands NOTE: use FDIVT2() instead!
|
romsub $af27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands NOTE: use FDIVT2() instead!
|
||||||
romsub $af2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
romsub $af2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||||
romsub $af2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
romsub $af2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
romsub $af30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $af30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
@ -66,7 +66,7 @@ romsub $af66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memo
|
|||||||
romsub $af69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
romsub $af69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||||
romsub $af6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
romsub $af6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||||
|
|
||||||
; X16 additions
|
; X16 additions TODO so.... not on c128 !?
|
||||||
romsub $af6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
romsub $af6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||||
romsub $af72 = FADDT2() clobbers(A,X,Y) ; fac1 += fac2
|
romsub $af72 = FADDT2() clobbers(A,X,Y) ; fac1 += fac2
|
||||||
romsub $af75 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
romsub $af75 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
||||||
@ -75,10 +75,10 @@ romsub $af7b = NEGFAC() clobbers(A) ; switch the sign of
|
|||||||
romsub $af7e = FMULTT2() clobbers(A,X,Y) ; fac1 *= fac2
|
romsub $af7e = FMULTT2() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
romsub $af81 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
romsub $af81 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||||
romsub $af84 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $af84 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
romsub $af87 = FDIVT2() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $af87 = FDIVT2() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||||
romsub $af8a = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $af8a = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
romsub $af8d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
romsub $af8d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
romsub $af90 = FLOAT() clobbers(A,X,Y) ; FAC = (u8).A
|
romsub $af90 = FLOAT() clobbers(A,X,Y) ; FAC = (s8).A
|
||||||
romsub $af93 = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
romsub $af93 = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
||||||
romsub $af9C = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
romsub $af9C = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
||||||
romsub $af9f = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $af9f = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
@ -134,8 +134,8 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
asmsub FREADUY (ubyte value @Y) {
|
asmsub FREADUY (ubyte value @Y) {
|
||||||
; -- 8 bit unsigned Y -> float in fac1
|
; -- 8 bit unsigned Y -> float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
tya
|
lda #0
|
||||||
jmp FLOAT
|
jmp GIVAYF
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +340,7 @@ _modified jsr $ffff ; modified
|
|||||||
_use_kernal .byte 0
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta IRQ_SCRATCH_ZPB1
|
sta IRQ_SCRATCH_ZPB1
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_REG
|
||||||
@ -354,18 +353,15 @@ _irq_handler_init
|
|||||||
sta IRQ_SCRATCH_ZPWORD2
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||||
dex
|
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||||
dex
|
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||||
dex
|
ldx #32
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
cld
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler_end
|
_irq_handler_end
|
||||||
; restore all zp scratch registers and the X register
|
; restore all zp scratch registers
|
||||||
lda IRQ_SCRATCH_ZPB1
|
lda IRQ_SCRATCH_ZPB1
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda IRQ_SCRATCH_ZPREG
|
lda IRQ_SCRATCH_ZPREG
|
||||||
@ -378,10 +374,8 @@ _irq_handler_end
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
|
@ -6,7 +6,7 @@ FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
|||||||
|
|
||||||
|
|
||||||
floats_store_reg .byte 0 ; temp storage
|
floats_store_reg .byte 0 ; temp storage
|
||||||
|
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||||
|
|
||||||
ub2float .proc
|
ub2float .proc
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
|
@ -60,8 +60,8 @@ romsub $b853 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1
|
|||||||
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||||
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||||
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||||
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||||
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
|
@ -384,8 +384,7 @@ _modified jsr $ffff ; modified
|
|||||||
_use_kernal .byte 0
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta IRQ_SCRATCH_ZPB1
|
sta IRQ_SCRATCH_ZPB1
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_REG
|
||||||
@ -398,18 +397,15 @@ _irq_handler_init
|
|||||||
sta IRQ_SCRATCH_ZPWORD2
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||||
dex
|
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||||
dex
|
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||||
dex
|
ldx #32
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
cld
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler_end
|
_irq_handler_end
|
||||||
; restore all zp scratch registers and the X register
|
; restore all zp scratch registers
|
||||||
lda IRQ_SCRATCH_ZPB1
|
lda IRQ_SCRATCH_ZPB1
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda IRQ_SCRATCH_ZPREG
|
lda IRQ_SCRATCH_ZPREG
|
||||||
@ -422,10 +418,8 @@ _irq_handler_end
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
|
@ -40,8 +40,8 @@ romsub $fe18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt valu
|
|||||||
romsub $fe1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
romsub $fe1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||||
romsub $fe1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
romsub $fe1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||||
romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||||
romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||||
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
@ -76,7 +76,7 @@ romsub $fe7b = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
|||||||
romsub $fe7e = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $fe7e = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
romsub $fe81 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $fe81 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
romsub $fe84 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
romsub $fe84 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
romsub $fe87 = FLOAT() clobbers(A,X,Y) ; FAC = (u8).A
|
romsub $fe87 = FLOAT() clobbers(A,X,Y) ; FAC = (s8).A
|
||||||
romsub $fe8a = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
romsub $fe8a = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
||||||
romsub $fe8d = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
romsub $fe8d = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
||||||
romsub $fe90 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $fe90 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
@ -133,8 +133,8 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
asmsub FREADUY (ubyte value @Y) {
|
asmsub FREADUY (ubyte value @Y) {
|
||||||
; -- 8 bit unsigned Y -> float in fac1
|
; -- 8 bit unsigned Y -> float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
tya
|
lda #0
|
||||||
jmp FLOAT
|
jmp GIVAYF
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ graphics {
|
|||||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cx16.FB_cursor_position
|
jsr cx16.FB_cursor_position
|
||||||
lda #1
|
lda graphics.stroke_color
|
||||||
jsr cx16.FB_set_pixel
|
jsr cx16.FB_set_pixel
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -233,14 +233,18 @@ cx16 {
|
|||||||
&ubyte VERA_IEN = VERA_BASE + $0006
|
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||||
&ubyte VERA_ISR = VERA_BASE + $0007
|
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009 ; DCSEL= 0
|
||||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A ; DCSEL= 0
|
||||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B ; DCSEL= 0
|
||||||
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
&ubyte VERA_DC_BORDER = VERA_BASE + $000C ; DCSEL= 0
|
||||||
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
&ubyte VERA_DC_HSTART = VERA_BASE + $0009 ; DCSEL= 1
|
||||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A ; DCSEL= 1
|
||||||
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
&ubyte VERA_DC_VSTART = VERA_BASE + $000B ; DCSEL= 1
|
||||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C ; DCSEL= 1
|
||||||
|
&ubyte VERA_DC_VER0 = VERA_BASE + $0009 ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER1 = VERA_BASE + $000A ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER2 = VERA_BASE + $000B ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER3 = VERA_BASE + $000C ; DCSEL=63
|
||||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||||
@ -266,41 +270,41 @@ cx16 {
|
|||||||
|
|
||||||
; I/O
|
; I/O
|
||||||
|
|
||||||
const uword via1 = $9f00 ;VIA 6522 #1
|
const uword VIA1_BASE = $9f00 ;VIA 6522 #1
|
||||||
&ubyte d1prb = via1+0
|
&ubyte via1prb = VIA1_BASE + 0
|
||||||
&ubyte d1pra = via1+1
|
&ubyte via1pra = VIA1_BASE + 1
|
||||||
&ubyte d1ddrb = via1+2
|
&ubyte via1ddrb = VIA1_BASE + 2
|
||||||
&ubyte d1ddra = via1+3
|
&ubyte via1ddra = VIA1_BASE + 3
|
||||||
&ubyte d1t1l = via1+4
|
&ubyte via1t1l = VIA1_BASE + 4
|
||||||
&ubyte d1t1h = via1+5
|
&ubyte via1t1h = VIA1_BASE + 5
|
||||||
&ubyte d1t1ll = via1+6
|
&ubyte via1t1ll = VIA1_BASE + 6
|
||||||
&ubyte d1t1lh = via1+7
|
&ubyte via1t1lh = VIA1_BASE + 7
|
||||||
&ubyte d1t2l = via1+8
|
&ubyte via1t2l = VIA1_BASE + 8
|
||||||
&ubyte d1t2h = via1+9
|
&ubyte via1t2h = VIA1_BASE + 9
|
||||||
&ubyte d1sr = via1+10
|
&ubyte via1sr = VIA1_BASE + 10
|
||||||
&ubyte d1acr = via1+11
|
&ubyte via1acr = VIA1_BASE + 11
|
||||||
&ubyte d1pcr = via1+12
|
&ubyte via1pcr = VIA1_BASE + 12
|
||||||
&ubyte d1ifr = via1+13
|
&ubyte via1ifr = VIA1_BASE + 13
|
||||||
&ubyte d1ier = via1+14
|
&ubyte via1ier = VIA1_BASE + 14
|
||||||
&ubyte d1ora = via1+15
|
&ubyte via1ora = VIA1_BASE + 15
|
||||||
|
|
||||||
const uword via2 = $9f10 ;VIA 6522 #2
|
const uword VIA2_BASE = $9f10 ;VIA 6522 #2
|
||||||
&ubyte d2prb = via2+0
|
&ubyte via2prb = VIA2_BASE + 0
|
||||||
&ubyte d2pra = via2+1
|
&ubyte via2pra = VIA2_BASE + 1
|
||||||
&ubyte d2ddrb = via2+2
|
&ubyte via2ddrb = VIA2_BASE + 2
|
||||||
&ubyte d2ddra = via2+3
|
&ubyte via2ddra = VIA2_BASE + 3
|
||||||
&ubyte d2t1l = via2+4
|
&ubyte via2t1l = VIA2_BASE + 4
|
||||||
&ubyte d2t1h = via2+5
|
&ubyte via2t1h = VIA2_BASE + 5
|
||||||
&ubyte d2t1ll = via2+6
|
&ubyte via2t1ll = VIA2_BASE + 6
|
||||||
&ubyte d2t1lh = via2+7
|
&ubyte via2t1lh = VIA2_BASE + 7
|
||||||
&ubyte d2t2l = via2+8
|
&ubyte via2t2l = VIA2_BASE + 8
|
||||||
&ubyte d2t2h = via2+9
|
&ubyte via2t2h = VIA2_BASE + 9
|
||||||
&ubyte d2sr = via2+10
|
&ubyte via2sr = VIA2_BASE + 10
|
||||||
&ubyte d2acr = via2+11
|
&ubyte via2acr = VIA2_BASE + 11
|
||||||
&ubyte d2pcr = via2+12
|
&ubyte via2pcr = VIA2_BASE + 12
|
||||||
&ubyte d2ifr = via2+13
|
&ubyte via2ifr = VIA2_BASE + 13
|
||||||
&ubyte d2ier = via2+14
|
&ubyte via2ier = VIA2_BASE + 14
|
||||||
&ubyte d2ora = via2+15
|
&ubyte via2ora = VIA2_BASE + 15
|
||||||
|
|
||||||
; YM-2151 sound chip
|
; YM-2151 sound chip
|
||||||
&ubyte YM_ADDRESS = $9f40
|
&ubyte YM_ADDRESS = $9f40
|
||||||
@ -345,7 +349,7 @@ romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias f
|
|||||||
; framebuffer
|
; framebuffer
|
||||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||||
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
|
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
|
||||||
romsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte bytecount @X) clobbers(A,X,Y)
|
romsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte colorcount @X) clobbers(A,X,Y)
|
||||||
romsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
|
romsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
|
||||||
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the previous routine, but avoiding having to respecify both x and y every time
|
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the previous routine, but avoiding having to respecify both x and y every time
|
||||||
romsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
|
romsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
|
||||||
@ -394,6 +398,14 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
|||||||
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||||
|
|
||||||
|
; Audio (bank 10)
|
||||||
|
romsub $C04B = psg_init() clobbers(A,X,Y)
|
||||||
|
romsub $C063 = ym_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)init YM chip
|
||||||
|
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> ubyte @Pc ; load default YM patches
|
||||||
|
romsub $C09F = audio_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)initialize PSG and YM audio chips
|
||||||
|
; TODO: add more of the audio routines?
|
||||||
|
|
||||||
|
|
||||||
asmsub kbdbuf_clear() {
|
asmsub kbdbuf_clear() {
|
||||||
; -- convenience helper routine to clear the keyboard buffer
|
; -- convenience helper routine to clear the keyboard buffer
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -641,8 +653,9 @@ asmsub init_system() {
|
|||||||
lda VERA_DC_VIDEO
|
lda VERA_DC_VIDEO
|
||||||
and #%00000111 ; retain chroma + output mode
|
and #%00000111 ; retain chroma + output mode
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
lda #$80
|
lda #$0a
|
||||||
sta VERA_CTRL ; reset vera
|
sta $01 ; rom bank 10 (audio)
|
||||||
|
jsr audio_init ; silence
|
||||||
stz $01 ; rom bank 0 (kernal)
|
stz $01 ; rom bank 0 (kernal)
|
||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr c64.RESTOR
|
||||||
@ -650,7 +663,7 @@ asmsub init_system() {
|
|||||||
lda VERA_DC_VIDEO
|
lda VERA_DC_VIDEO
|
||||||
and #%11111000
|
and #%11111000
|
||||||
ora P8ZP_SCRATCH_REG
|
ora P8ZP_SCRATCH_REG
|
||||||
sta VERA_DC_VIDEO ; keep old output mode
|
sta VERA_DC_VIDEO ; restore old output mode
|
||||||
lda #$90 ; black
|
lda #$90 ; black
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
lda #1
|
lda #1
|
||||||
@ -729,8 +742,7 @@ _modified jsr $ffff ; modified
|
|||||||
_use_kernal .byte 0
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta IRQ_SCRATCH_ZPB1
|
sta IRQ_SCRATCH_ZPB1
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_REG
|
||||||
@ -743,18 +755,15 @@ _irq_handler_init
|
|||||||
sta IRQ_SCRATCH_ZPWORD2
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||||
dex
|
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||||
dex
|
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||||
dex
|
ldx #32
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
cld
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler_end
|
_irq_handler_end
|
||||||
; restore all zp scratch registers and the X register
|
; restore all zp scratch registers
|
||||||
lda IRQ_SCRATCH_ZPB1
|
lda IRQ_SCRATCH_ZPB1
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda IRQ_SCRATCH_ZPREG
|
lda IRQ_SCRATCH_ZPREG
|
||||||
@ -767,10 +776,8 @@ _irq_handler_end
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
@ -913,8 +920,6 @@ sys {
|
|||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
stz $01 ; bank the kernal in
|
stz $01 ; bank the kernal in
|
||||||
lda #$80
|
|
||||||
sta cx16.VERA_CTRL ; reset Vera (kernal doesn't do this?)
|
|
||||||
jmp (cx16.RESET_VEC)
|
jmp (cx16.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -997,6 +997,20 @@ _arg_s1 .word 0
|
|||||||
_arg_s2 .word 0
|
_arg_s2 .word 0
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
strcmp_stack .proc
|
||||||
|
; -- compare strings, both on stack.
|
||||||
|
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
ldy P8ESTACK_HI,x
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
sty P8ZP_SCRATCH_W2+1
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
ldy P8ESTACK_HI,x
|
||||||
|
jmp strcmp_mem
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
strcmp_mem .proc
|
strcmp_mem .proc
|
||||||
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
||||||
|
@ -198,7 +198,7 @@ sub str2uword(str string) -> uword {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.w r65500,conv.str2uword.string
|
loadm.w r65500,conv.str2uword.string
|
||||||
syscall 11
|
syscall 11
|
||||||
return
|
returnreg.w r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ sub str2word(str string) -> word {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.w r65500,conv.str2word.string
|
loadm.w r65500,conv.str2word.string
|
||||||
syscall 12
|
syscall 12
|
||||||
return
|
returnreg.w r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ sub pow(float value, float power) -> float {
|
|||||||
loadm.f fr0,floats.pow.value
|
loadm.f fr0,floats.pow.value
|
||||||
loadm.f fr1,floats.pow.power
|
loadm.f fr1,floats.pow.power
|
||||||
fpow.f fr0,fr1
|
fpow.f fr0,fr1
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ sub fabs(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.fabs.value
|
loadm.f fr0,floats.fabs.value
|
||||||
fabs.f fr0,fr0
|
fabs.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ sub sin(float angle) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.sin.angle
|
loadm.f fr0,floats.sin.angle
|
||||||
fsin.f fr0,fr0
|
fsin.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ sub cos(float angle) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.cos.angle
|
loadm.f fr0,floats.cos.angle
|
||||||
fcos.f fr0,fr0
|
fcos.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ sub tan(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.tan.value
|
loadm.f fr0,floats.tan.value
|
||||||
ftan.f fr0,fr0
|
ftan.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ sub atan(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.atan.value
|
loadm.f fr0,floats.atan.value
|
||||||
fatan.f fr0,fr0
|
fatan.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ sub ln(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.ln.value
|
loadm.f fr0,floats.ln.value
|
||||||
fln.f fr0,fr0
|
fln.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ sub log2(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.log2.value
|
loadm.f fr0,floats.log2.value
|
||||||
flog.f fr0,fr0
|
flog.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ sub sqrt(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.sqrt.value
|
loadm.f fr0,floats.sqrt.value
|
||||||
sqrt.f fr0,fr0
|
sqrt.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ sub round(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.round.value
|
loadm.f fr0,floats.round.value
|
||||||
fround.f fr0,fr0
|
fround.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ sub floor(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.floor.value
|
loadm.f fr0,floats.floor.value
|
||||||
ffloor.f fr0,fr0
|
ffloor.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,14 +120,14 @@ sub ceil(float value) -> float {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.f fr0,floats.ceil.value
|
loadm.f fr0,floats.ceil.value
|
||||||
fceil.f fr0,fr0
|
fceil.f fr0,fr0
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rndf() -> float {
|
sub rndf() -> float {
|
||||||
%ir {{
|
%ir {{
|
||||||
syscall 35
|
syscall 35
|
||||||
return
|
returnreg.f fr0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,14 +162,14 @@ math {
|
|||||||
sub rnd() -> ubyte {
|
sub rnd() -> ubyte {
|
||||||
%ir {{
|
%ir {{
|
||||||
syscall 33
|
syscall 33
|
||||||
return
|
returnreg.b r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rndw() -> uword {
|
sub rndw() -> uword {
|
||||||
%ir {{
|
%ir {{
|
||||||
syscall 34
|
syscall 34
|
||||||
return
|
returnreg.w r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ string {
|
|||||||
loadm.w r65500,string.compare.st1
|
loadm.w r65500,string.compare.st1
|
||||||
loadm.w r65501,string.compare.st2
|
loadm.w r65501,string.compare.st2
|
||||||
syscall 29
|
syscall 29
|
||||||
return
|
returnreg.b r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ sys {
|
|||||||
loadm.w r65500,sys.gfx_getpixel.xx
|
loadm.w r65500,sys.gfx_getpixel.xx
|
||||||
loadm.w r65501,sys.gfx_getpixel.yy
|
loadm.w r65501,sys.gfx_getpixel.yy
|
||||||
syscall 30
|
syscall 30
|
||||||
return
|
returnreg.b r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
|
||||||
|
sub width() -> ubyte {
|
||||||
|
return 80 ; just some chosen value for the 'width' of the console
|
||||||
|
}
|
||||||
|
|
||||||
|
sub height() -> ubyte {
|
||||||
|
return 30 ; just some chosen value for the 'height' of the console
|
||||||
|
}
|
||||||
|
|
||||||
sub clear_screen() {
|
sub clear_screen() {
|
||||||
str @shared sequence = "\x1b[2J\x1B[H"
|
str @shared sequence = "\x1b[2J\x1B[H"
|
||||||
%ir {{
|
%ir {{
|
||||||
@ -116,7 +124,7 @@ sub input_chars (uword buffer) -> ubyte {
|
|||||||
%ir {{
|
%ir {{
|
||||||
loadm.w r65500,txt.input_chars.buffer
|
loadm.w r65500,txt.input_chars.buffer
|
||||||
syscall 6
|
syscall 6
|
||||||
return
|
returnreg.b r0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
8.10
|
8.11
|
||||||
|
@ -52,6 +52,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||||
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
|
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
|
||||||
|
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
|
||||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -126,6 +127,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
varsHigh == true,
|
varsHigh == true,
|
||||||
|
useNewExprCode == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
processedSymbols,
|
processedSymbols,
|
||||||
@ -190,6 +192,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
varsHigh == true,
|
varsHigh == true,
|
||||||
|
useNewExprCode == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
processedSymbols,
|
processedSymbols,
|
||||||
|
@ -4,11 +4,12 @@ import com.github.michaelbull.result.onFailure
|
|||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.*
|
import prog8.code.target.*
|
||||||
import prog8.codegen.vm.VmCodeGen
|
import prog8.codegen.vm.VmCodeGen
|
||||||
@ -36,6 +37,7 @@ class CompilerArguments(val filepath: Path,
|
|||||||
val asmListfile: Boolean,
|
val asmListfile: Boolean,
|
||||||
val experimentalCodegen: Boolean,
|
val experimentalCodegen: Boolean,
|
||||||
val varsHigh: Boolean,
|
val varsHigh: Boolean,
|
||||||
|
val useNewExprCode: Boolean,
|
||||||
val compilationTarget: String,
|
val compilationTarget: String,
|
||||||
val evalStackBaseAddress: UInt?,
|
val evalStackBaseAddress: UInt?,
|
||||||
val symbolDefs: Map<String, String>,
|
val symbolDefs: Map<String, String>,
|
||||||
@ -76,6 +78,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
asmListfile = args.asmListfile
|
asmListfile = args.asmListfile
|
||||||
experimentalCodegen = args.experimentalCodegen
|
experimentalCodegen = args.experimentalCodegen
|
||||||
varsHigh = args.varsHigh
|
varsHigh = args.varsHigh
|
||||||
|
useNewExprCode = args.useNewExprCode
|
||||||
evalStackBaseAddress = args.evalStackBaseAddress
|
evalStackBaseAddress = args.evalStackBaseAddress
|
||||||
outputDir = args.outputDir.normalize()
|
outputDir = args.outputDir.normalize()
|
||||||
symbolDefs = args.symbolDefs
|
symbolDefs = args.symbolDefs
|
||||||
@ -118,7 +121,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
|
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
|
||||||
// printProgram(program)
|
// printProgram(program)
|
||||||
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
|
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
|
||||||
// printAst(intermediateAst, ::println)
|
// printAst(intermediateAst, true, ::println)
|
||||||
|
|
||||||
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) {
|
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) {
|
||||||
System.err.println("Error in codegeneration or assembler")
|
System.err.println("Error in codegeneration or assembler")
|
||||||
@ -373,6 +376,9 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
|||||||
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
val remover2 = UnusedCodeRemover(program, errors, compTarget)
|
||||||
|
remover2.visit(program)
|
||||||
|
remover2.applyModifications()
|
||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +408,18 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
|||||||
else if (compilerOptions.compTarget.name == VMTarget.NAME)
|
else if (compilerOptions.compTarget.name == VMTarget.NAME)
|
||||||
VmCodeGen()
|
VmCodeGen()
|
||||||
else
|
else
|
||||||
throw NotImplementedError("no asm generator for cpu ${compilerOptions.compTarget.machine.cpu}")
|
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
|
||||||
|
|
||||||
|
if(compilerOptions.useNewExprCode) {
|
||||||
|
if(compilerOptions.compTarget.machine.cpu !in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
|
||||||
|
// the IR code gen backend has its own, better, version of dealing with binary expressions.
|
||||||
|
throw IllegalArgumentException("'newexpr' expression rewrite should not be used with compilation target ${compilerOptions.compTarget.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
transformNewExpressions(program)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printAst(program, true) { println(it) }
|
||||||
|
|
||||||
val stMaker = SymbolTableMaker(program, compilerOptions)
|
val stMaker = SymbolTableMaker(program, compilerOptions)
|
||||||
val symbolTable = stMaker.make()
|
val symbolTable = stMaker.make()
|
||||||
@ -410,8 +427,193 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
|||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
return if(assembly!=null && errors.noErrors()) {
|
return if(assembly!=null && errors.noErrors()) {
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions, errors)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun transformNewExpressions(program: PtProgram) {
|
||||||
|
val newVariables = mutableMapOf<PtSub, MutableList<PtVariable>>()
|
||||||
|
var countByteVars = 0
|
||||||
|
var countWordVars = 0
|
||||||
|
var countFloatVars = 0
|
||||||
|
// TODO: find a reliable way to reuse more temp vars across expressions
|
||||||
|
|
||||||
|
fun getExprVar(type: DataType, pos: Position, scope: PtSub): PtIdentifier {
|
||||||
|
val count = when(type) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
countByteVars++
|
||||||
|
countByteVars
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
countWordVars++
|
||||||
|
countWordVars
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
countFloatVars++
|
||||||
|
countFloatVars
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("weird dt")
|
||||||
|
}
|
||||||
|
val name = "p8p_exprvar_${count}_${type.toString().lowercase()}"
|
||||||
|
var subVars = newVariables[scope]
|
||||||
|
if(subVars==null) {
|
||||||
|
subVars = mutableListOf()
|
||||||
|
newVariables[scope] = subVars
|
||||||
|
}
|
||||||
|
if(subVars.all { it.name!=name }) {
|
||||||
|
subVars.add(PtVariable(name, type, ZeropageWish.DONTCARE, null, null, pos))
|
||||||
|
}
|
||||||
|
return PtIdentifier("${scope.scopedName}.$name", type, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transformExpr(expr: PtBinaryExpression): Pair<PtExpression, List<IPtAssignment>> {
|
||||||
|
// depth first process the expression tree
|
||||||
|
val scope = expr.definingSub()!!
|
||||||
|
val assignments = mutableListOf<IPtAssignment>()
|
||||||
|
|
||||||
|
fun transformOperand(node: PtExpression): PtNode {
|
||||||
|
return when(node) {
|
||||||
|
is PtNumber, is PtIdentifier, is PtArray, is PtString, is PtMachineRegister -> node
|
||||||
|
is PtBinaryExpression -> {
|
||||||
|
val (replacement, subAssigns) = transformExpr(node)
|
||||||
|
assignments.addAll(subAssigns)
|
||||||
|
replacement
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val variable = getExprVar(node.type, node.position, scope)
|
||||||
|
val assign = PtAssignment(node.position)
|
||||||
|
val target = PtAssignTarget(variable.position)
|
||||||
|
target.add(variable)
|
||||||
|
assign.add(target)
|
||||||
|
assign.add(node)
|
||||||
|
assignments.add(assign)
|
||||||
|
variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newLeft = transformOperand(expr.left)
|
||||||
|
val newRight = transformOperand(expr.right)
|
||||||
|
|
||||||
|
// process the binexpr
|
||||||
|
|
||||||
|
val resultVar =
|
||||||
|
if(expr.type == expr.left.type) {
|
||||||
|
getExprVar(expr.type, expr.position, scope)
|
||||||
|
} else {
|
||||||
|
if(expr.operator in ComparisonOperators && expr.type in ByteDatatypes) {
|
||||||
|
// this is very common and should be dealth with correctly; byte==0, word>42
|
||||||
|
val varType = if(expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
|
||||||
|
getExprVar(varType, expr.position, scope)
|
||||||
|
}
|
||||||
|
else if(expr.left.type in PassByReferenceDatatypes && expr.type==DataType.UBYTE) {
|
||||||
|
// this is common and should be dealth with correctly; for instance "name"=="john"
|
||||||
|
val varType = if (expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
|
||||||
|
getExprVar(varType, expr.position, scope)
|
||||||
|
} else if(expr.left.type equalsSize expr.type) {
|
||||||
|
getExprVar(expr.type, expr.position, scope)
|
||||||
|
} else {
|
||||||
|
TODO("expression type differs from left operand type! got ${expr.left.type} expected ${expr.type} ${expr.position}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resultVar.name!=(newLeft as? PtIdentifier)?.name) {
|
||||||
|
// resultvar = left
|
||||||
|
val assign1 = PtAssignment(newLeft.position)
|
||||||
|
val target1 = PtAssignTarget(resultVar.position)
|
||||||
|
target1.add(resultVar)
|
||||||
|
assign1.add(target1)
|
||||||
|
assign1.add(newLeft)
|
||||||
|
assignments.add(assign1)
|
||||||
|
}
|
||||||
|
// resultvar {oper}= right
|
||||||
|
val operator = if(expr.operator in ComparisonOperators) expr.operator else expr.operator+'='
|
||||||
|
val assign2 = PtAugmentedAssign(operator, newRight.position)
|
||||||
|
val target2 = PtAssignTarget(resultVar.position)
|
||||||
|
target2.add(resultVar.copy())
|
||||||
|
assign2.add(target2)
|
||||||
|
assign2.add(newRight)
|
||||||
|
assignments.add(assign2)
|
||||||
|
return Pair(resultVar, assignments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isProperStatement(node: PtNode): Boolean {
|
||||||
|
return when(node) {
|
||||||
|
is PtAssignment -> true
|
||||||
|
is PtAugmentedAssign -> true
|
||||||
|
is PtBreakpoint -> true
|
||||||
|
is PtConditionalBranch -> true
|
||||||
|
is PtForLoop -> true
|
||||||
|
is PtIfElse -> true
|
||||||
|
is PtIncludeBinary -> true
|
||||||
|
is PtInlineAssembly -> true
|
||||||
|
is PtJump -> true
|
||||||
|
is PtAsmSub -> true
|
||||||
|
is PtLabel -> true
|
||||||
|
is PtSub -> true
|
||||||
|
is PtVariable -> true
|
||||||
|
is PtNop -> true
|
||||||
|
is PtPostIncrDecr -> true
|
||||||
|
is PtRepeatLoop -> true
|
||||||
|
is PtReturn -> true
|
||||||
|
is PtWhen -> true
|
||||||
|
is PtBuiltinFunctionCall -> node.void
|
||||||
|
is PtFunctionCall -> node.void
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transform(node: PtNode, parent: PtNode) {
|
||||||
|
if(node is PtBinaryExpression) {
|
||||||
|
node.children.toTypedArray().forEach {
|
||||||
|
transform(it, node)
|
||||||
|
}
|
||||||
|
val (rep, assignments) = transformExpr(node)
|
||||||
|
var replacement = rep
|
||||||
|
if(!(rep.type equalsSize node.type)) {
|
||||||
|
if(rep.type in NumericDatatypes && node.type in ByteDatatypes) {
|
||||||
|
replacement = PtTypeCast(node.type, node.position)
|
||||||
|
replacement.add(rep)
|
||||||
|
} else
|
||||||
|
TODO("cast replacement type ${rep.type} -> ${node.type}")
|
||||||
|
}
|
||||||
|
var idx = parent.children.indexOf(node)
|
||||||
|
parent.children[idx] = replacement
|
||||||
|
replacement.parent = parent
|
||||||
|
// find the statement above which we should insert the assignments
|
||||||
|
var stmt = node
|
||||||
|
while(!isProperStatement(stmt))
|
||||||
|
stmt = stmt.parent
|
||||||
|
idx = stmt.parent.children.indexOf(stmt)
|
||||||
|
assignments.reversed().forEach {
|
||||||
|
stmt.parent.add(idx, it as PtNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.children.toTypedArray().forEach { child -> transform(child, node) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program.allBlocks().forEach { block ->
|
||||||
|
block.children.toTypedArray().forEach {
|
||||||
|
transform(it, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new variables
|
||||||
|
newVariables.forEach { (sub, vars) ->
|
||||||
|
vars.forEach {
|
||||||
|
sub.add(0, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extra check to see that all PtBinaryExpressions have been transformed
|
||||||
|
fun binExprCheck(node: PtNode) {
|
||||||
|
if(node is PtBinaryExpression)
|
||||||
|
throw IllegalArgumentException("still got binexpr $node ${node.position}")
|
||||||
|
node.children.forEach { binExprCheck(it) }
|
||||||
|
}
|
||||||
|
binExprCheck(program)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ class AsmInstructionNamesReplacer(
|
|||||||
return if(newName!=subroutine.name || changedParams.isNotEmpty()) {
|
return if(newName!=subroutine.name || changedParams.isNotEmpty()) {
|
||||||
val newSub = Subroutine(newName, subroutine.parameters, subroutine.returntypes,
|
val newSub = Subroutine(newName, subroutine.parameters, subroutine.returntypes,
|
||||||
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, subroutine.asmAddress, subroutine.isAsmSubroutine,
|
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, subroutine.asmAddress, subroutine.isAsmSubroutine,
|
||||||
subroutine.inline, subroutine.statements, subroutine.position)
|
subroutine.inline, false, subroutine.statements, subroutine.position)
|
||||||
if(changedParams.isNotEmpty())
|
if(changedParams.isNotEmpty())
|
||||||
subsWithParamRefsToFix += newSub
|
subsWithParamRefsToFix += newSub
|
||||||
listOf(IAstModification.ReplaceNode(subroutine, newSub, parent))
|
listOf(IAstModification.ReplaceNode(subroutine, newSub, parent))
|
||||||
|
@ -6,6 +6,7 @@ import prog8.ast.base.SyntaxError
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.ast.PtIdentifier
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
@ -60,7 +61,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(identifier: IdentifierReference) {
|
override fun visit(identifier: IdentifierReference) {
|
||||||
val target = identifier.targetVarDecl(program)
|
val stmt = identifier.targetStatement(program)
|
||||||
|
if(stmt==null)
|
||||||
|
errors.err("undefined symbol: ${identifier.nameInSource.joinToString(".")}", identifier.position)
|
||||||
|
else {
|
||||||
|
val target = stmt as? VarDecl
|
||||||
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
|
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
|
||||||
if (target.definingSubroutine!!.isAsmSubroutine) {
|
if (target.definingSubroutine!!.isAsmSubroutine) {
|
||||||
if (target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() })
|
if (target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() })
|
||||||
@ -68,6 +73,20 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(unrollLoop: UnrollLoop) {
|
||||||
|
if(unrollLoop.iterations<0 || unrollLoop.iterations>65535)
|
||||||
|
errors.err("invalid number of unrolls", unrollLoop.position)
|
||||||
|
unrollLoop.body.statements.forEach {
|
||||||
|
if(it !is InlineAssembly && it !is Assignment && it !is BuiltinFunctionCallStatement && it !is FunctionCallStatement && it !is PostIncrDecr)
|
||||||
|
errors.err("invalid statement in unroll loop", it.position)
|
||||||
|
}
|
||||||
|
if(unrollLoop.iterations * unrollLoop.body.statements.size > 256) {
|
||||||
|
errors.warn("large number of unrolls, potential code size issue", unrollLoop.position)
|
||||||
|
}
|
||||||
|
super.visit(unrollLoop)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(returnStmt: Return) {
|
override fun visit(returnStmt: Return) {
|
||||||
val expectedReturnValues = returnStmt.definingSubroutine?.returntypes ?: emptyList()
|
val expectedReturnValues = returnStmt.definingSubroutine?.returntypes ?: emptyList()
|
||||||
@ -442,6 +461,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
val iterations = repeatLoop.iterations?.constValue(program)
|
val iterations = repeatLoop.iterations?.constValue(program)
|
||||||
if(iterations != null && iterations.number.toInt() > 65535)
|
if(iterations != null && iterations.number.toInt() > 65535)
|
||||||
errors.err("repeat cannot go over 65535 iterations", iterations.position)
|
errors.err("repeat cannot go over 65535 iterations", iterations.position)
|
||||||
|
|
||||||
|
val ident = repeatLoop.iterations as? IdentifierReference
|
||||||
|
if(ident!=null) {
|
||||||
|
val targetVar = ident.targetVarDecl(program)
|
||||||
|
if(targetVar==null)
|
||||||
|
errors.err("invalid assignment value, maybe forgot '&' (address-of)", ident.position)
|
||||||
|
}
|
||||||
super.visit(repeatLoop)
|
super.visit(repeatLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,9 +652,19 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||||
if (declValue.inferType(program) isnot decl.datatype) {
|
val iDt = declValue.inferType(program)
|
||||||
|
if (iDt isnot decl.datatype) {
|
||||||
|
if(decl.datatype in ArrayDatatypes) {
|
||||||
|
val eltDt = ArrayToElementTypes.getValue(decl.datatype)
|
||||||
|
if(iDt isnot eltDt) {
|
||||||
|
if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL))
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if(!(iDt.isBool && decl.datatype==DataType.UBYTE || iDt.istype(DataType.UBYTE) && decl.datatype==DataType.BOOL))
|
||||||
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// array length limits and constant lenghts
|
// array length limits and constant lenghts
|
||||||
@ -897,6 +933,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
||||||
}
|
}
|
||||||
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
|
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
|
||||||
|
"<<", ">>" -> {
|
||||||
|
if(rightDt in WordDatatypes) {
|
||||||
|
val shift = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(shift==null || shift > 255) {
|
||||||
|
errors.err("shift by a word value not supported, max is a byte", expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftDt !in NumericDatatypes && leftDt != DataType.STR && leftDt != DataType.BOOL)
|
if(leftDt !in NumericDatatypes && leftDt != DataType.STR && leftDt != DataType.BOOL)
|
||||||
@ -905,17 +949,23 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("right operand is not numeric or str", expr.right.position)
|
errors.err("right operand is not numeric or str", expr.right.position)
|
||||||
if(leftDt!=rightDt) {
|
if(leftDt!=rightDt) {
|
||||||
if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") {
|
if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") {
|
||||||
// only exception allowed: str * constvalue
|
// exception allowed: str * constvalue
|
||||||
if(expr.right.constValue(program)==null)
|
if(expr.right.constValue(program)==null)
|
||||||
errors.err("can only use string repeat with a constant number value", expr.left.position)
|
errors.err("can only use string repeat with a constant number value", expr.left.position)
|
||||||
} else if(leftDt==DataType.BOOL && rightDt in ByteDatatypes || leftDt in ByteDatatypes && rightDt==DataType.BOOL) {
|
} else if(leftDt==DataType.BOOL && rightDt in ByteDatatypes || leftDt in ByteDatatypes && rightDt==DataType.BOOL) {
|
||||||
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
|
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
|
||||||
|
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
|
||||||
|
// exception allowed: shifting a word by a byte
|
||||||
} else {
|
} else {
|
||||||
errors.err("left and right operands aren't the same type", expr.left.position)
|
errors.err("left and right operands aren't the same type", expr.left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expr.operator !in ComparisonOperators) {
|
if(expr.operator in ComparisonOperators) {
|
||||||
|
if(leftDt!=rightDt && !(leftDt in ByteDatatypes && rightDt in ByteDatatypes)) {
|
||||||
|
throw FatalAstException("got comparison with different operand types: $leftDt ${expr.operator} $rightDt ${expr.position}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
|
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
|
||||||
// str+str and str*number have already been const evaluated before we get here.
|
// str+str and str*number have already been const evaluated before we get here.
|
||||||
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
|
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
|
||||||
@ -1095,6 +1145,22 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(funcName[0] == "divmod") {
|
||||||
|
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
|
||||||
|
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
|
||||||
|
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
|
||||||
|
errors.err("all arguments must be ubyte", functionCallStatement.position)
|
||||||
|
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UBYTE})
|
||||||
|
errors.err("all arguments must be ubyte", functionCallStatement.position)
|
||||||
|
}
|
||||||
|
else if(funcName[0] == "divmodw") {
|
||||||
|
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
|
||||||
|
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
|
||||||
|
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
|
||||||
|
errors.err("all arguments must be uword", functionCallStatement.position)
|
||||||
|
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UWORD})
|
||||||
|
errors.err("all arguments must be uword", functionCallStatement.position)
|
||||||
|
}
|
||||||
|
|
||||||
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
||||||
// in-place modification, can't be done on literals
|
// in-place modification, can't be done on literals
|
||||||
@ -1316,7 +1382,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
else
|
else
|
||||||
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
||||||
}
|
}
|
||||||
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", target.position)
|
null -> errors.err("undefined symbol: ${target.nameInSource.joinToString(".")}", target.position)
|
||||||
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -1462,6 +1528,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.BOOL -> {
|
DataType.BOOL -> {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
val eltDt = ArrayToElementTypes.getValue(targetDt)
|
||||||
|
return checkValueTypeAndRange(eltDt, value)
|
||||||
|
}
|
||||||
else -> return err("value of type ${value.type} not compatible with $targetDt")
|
else -> return err("value of type ${value.type} not compatible with $targetDt")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -13,7 +13,9 @@ import prog8.code.core.IErrorReporter
|
|||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This checks for naming conflicts, not for correct symbol references yet.
|
||||||
|
*/
|
||||||
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||||
private val program: Program,
|
private val program: Program,
|
||||||
private val compTarget: ICompilationTarget
|
private val compTarget: ICompilationTarget
|
||||||
|
@ -30,7 +30,7 @@ internal class AstOnetimeTransforms(private val program: Program, private val op
|
|||||||
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// note: The CodeDesugarer already does something similar, but that is meant ONLY to take
|
// note: The CodeDesugarer already does something similar, but that is meant ONLY to take
|
||||||
// into account the case where the index value is a word type.
|
// into account the case where the index value is a word type.
|
||||||
// The replacement here is to fix missing cases in the 6502 codegen.
|
// The replacement here is to fix missing cases in the 6502 codegen. (VM codegen doesn't use this workaround)
|
||||||
// TODO make the 6502 codegen better so this workaround can be removed
|
// TODO make the 6502 codegen better so this workaround can be removed
|
||||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||||
|
@ -36,6 +36,19 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator in ComparisonOperators && expr.left.inferType(program) istype DataType.STR && expr.right.inferType(program) istype DataType.STR) {
|
||||||
|
// replace string comparison expressions with calls to string.compare()
|
||||||
|
val stringCompare = BuiltinFunctionCall(
|
||||||
|
IdentifierReference(listOf("prog8_lib_stringcompare"), expr.position),
|
||||||
|
mutableListOf(expr.left.copy(), expr.right.copy()), expr.position)
|
||||||
|
val zero = NumericLiteral.optimalInteger(0, expr.position)
|
||||||
|
val comparison = BinaryExpression(stringCompare, expr.operator, zero, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, comparison, parent))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
||||||
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||||
|
@ -139,6 +139,10 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
|||||||
errors.warn("shift always results in 0", expr.position)
|
errors.warn("shift always results in 0", expr.position)
|
||||||
if(dt.istype(DataType.UWORD) && shifts.number>=16.0)
|
if(dt.istype(DataType.UWORD) && shifts.number>=16.0)
|
||||||
errors.warn("shift always results in 0", expr.position)
|
errors.warn("shift always results in 0", expr.position)
|
||||||
|
if(shifts.number<=255.0 && shifts.type in WordDatatypes) {
|
||||||
|
val byteVal = NumericLiteral(DataType.UBYTE, shifts.number, shifts.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr.right, byteVal, expr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
|
@ -65,7 +65,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
|
|||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
|
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
|
||||||
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
|
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
|
||||||
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements,
|
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, false, subroutine.statements,
|
||||||
subroutine.position)
|
subroutine.position)
|
||||||
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
|
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ if CONDITION==0
|
|||||||
while CONDITION { STUFF }
|
while CONDITION { STUFF }
|
||||||
==>
|
==>
|
||||||
_whileloop:
|
_whileloop:
|
||||||
if CONDITION==0 goto _after
|
if INVERTED-CONDITION goto _after
|
||||||
STUFF
|
STUFF
|
||||||
goto _whileloop
|
goto _whileloop
|
||||||
_after:
|
_after:
|
||||||
|
@ -9,10 +9,7 @@ import prog8.ast.base.FatalAstException
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.BuiltinFunctions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.SourceCode
|
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -55,6 +52,7 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
|||||||
is Label -> transform(statement)
|
is Label -> transform(statement)
|
||||||
is PostIncrDecr -> transform(statement)
|
is PostIncrDecr -> transform(statement)
|
||||||
is RepeatLoop -> transform(statement)
|
is RepeatLoop -> transform(statement)
|
||||||
|
is UnrollLoop -> transform(statement)
|
||||||
is Return -> transform(statement)
|
is Return -> transform(statement)
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
if(statement.isAsmSubroutine)
|
if(statement.isAsmSubroutine)
|
||||||
@ -94,14 +92,12 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
|||||||
val srcExpr = srcAssign.value
|
val srcExpr = srcAssign.value
|
||||||
val (operator: String, augmentedValue: Expression?) = when(srcExpr) {
|
val (operator: String, augmentedValue: Expression?) = when(srcExpr) {
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(srcExpr.operator=="==" || srcExpr.operator=="%") {
|
if(srcExpr.left isSameAs srcAssign.target) {
|
||||||
// no special code possible for 'in-place comparison and result as boolean' or 'remainder'
|
val oper = if(srcExpr.operator in ComparisonOperators) srcExpr.operator else srcExpr.operator+'='
|
||||||
Pair("", null)
|
Pair(oper, srcExpr.right)
|
||||||
}
|
|
||||||
else if(srcExpr.left isSameAs srcAssign.target) {
|
|
||||||
Pair(srcExpr.operator+'=', srcExpr.right)
|
|
||||||
} else if(srcExpr.right isSameAs srcAssign.target) {
|
} else if(srcExpr.right isSameAs srcAssign.target) {
|
||||||
Pair(srcExpr.operator+'=', srcExpr.left)
|
val oper = if(srcExpr.operator in ComparisonOperators) srcExpr.operator else srcExpr.operator+'='
|
||||||
|
Pair(oper, srcExpr.left)
|
||||||
} else {
|
} else {
|
||||||
// either left or right is same as target, other combinations are not supported here
|
// either left or right is same as target, other combinations are not supported here
|
||||||
Pair("", null)
|
Pair("", null)
|
||||||
@ -325,6 +321,16 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
|||||||
return repeat
|
return repeat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun transform(srcUnroll: UnrollLoop): PtNodeGroup {
|
||||||
|
val result = PtNodeGroup()
|
||||||
|
repeat(srcUnroll.iterations) {
|
||||||
|
srcUnroll.body.statements.forEach {
|
||||||
|
result.add(transformStatement(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun transform(srcNode: Return): PtReturn {
|
private fun transform(srcNode: Return): PtReturn {
|
||||||
val ret = PtReturn(srcNode.position)
|
val ret = PtReturn(srcNode.position)
|
||||||
if(srcNode.value!=null)
|
if(srcNode.value!=null)
|
||||||
|
@ -110,9 +110,6 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
// Simplify the conditional expression, introduce simple assignments if required.
|
// Simplify the conditional expression, introduce simple assignments if required.
|
||||||
// This is REQUIRED for correct code generation on 6502 because evaluating certain expressions
|
|
||||||
// clobber the handful of temporary variables in the zeropage and leaving everything in one
|
|
||||||
// expression then results in invalid values being compared. VM Codegen doesn't suffer from this.
|
|
||||||
// NOTE: sometimes this increases code size because additional stores/loads are generated for the
|
// NOTE: sometimes this increases code size because additional stores/loads are generated for the
|
||||||
// intermediate variables. We assume these are optimized away from the resulting assembly code later.
|
// intermediate variables. We assume these are optimized away from the resulting assembly code later.
|
||||||
val simplify = simplifyConditionalExpression(binExpr)
|
val simplify = simplifyConditionalExpression(binExpr)
|
||||||
|
@ -23,9 +23,19 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
val valueDt = declValue.inferType(program)
|
val valueDt = declValue.inferType(program)
|
||||||
if(valueDt isnot decl.datatype) {
|
if(valueDt isnot decl.datatype) {
|
||||||
|
|
||||||
// don't add a typecast on an array initializer value
|
// don't add a typecast on an array initializer value, unless booleans
|
||||||
if(valueDt.isInteger && decl.datatype in ArrayDatatypes)
|
if(valueDt.isInteger && decl.datatype in ArrayDatatypes) {
|
||||||
|
if(decl.datatype == DataType.ARRAY_BOOL) {
|
||||||
|
val integer = declValue.constValue(program)?.number
|
||||||
|
if(integer!=null) {
|
||||||
|
val num = NumericLiteral(DataType.UBYTE, if(integer==0.0) 0.0 else 1.0, declValue.position)
|
||||||
|
num.parent = decl
|
||||||
|
decl.value = num
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return noModifications
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// don't add a typecast if the initializer value is inherently not assignable
|
// don't add a typecast if the initializer value is inherently not assignable
|
||||||
if(valueDt isNotAssignableTo decl.datatype)
|
if(valueDt isNotAssignableTo decl.datatype)
|
||||||
@ -60,15 +70,15 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
if (parent is Assignment) {
|
if (parent is Assignment) {
|
||||||
if (parent.target.inferType(program).isWords) {
|
if (parent.target.inferType(program).isWords) {
|
||||||
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
||||||
if(rightDt.isBytes)
|
// if(rightDt.isBytes)
|
||||||
modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
||||||
}
|
}
|
||||||
} else if (parent is TypecastExpression && parent.type == DataType.UWORD && parent.parent is Assignment) {
|
} else if (parent is TypecastExpression && parent.type == DataType.UWORD && parent.parent is Assignment) {
|
||||||
val assign = parent.parent as Assignment
|
val assign = parent.parent as Assignment
|
||||||
if (assign.target.inferType(program).isWords) {
|
if (assign.target.inferType(program).isWords) {
|
||||||
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
||||||
if(rightDt.isBytes)
|
// if(rightDt.isBytes)
|
||||||
modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(modifications.isNotEmpty())
|
if(modifications.isNotEmpty())
|
||||||
@ -140,6 +150,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if((expr.operator!="<<" && expr.operator!=">>") || !leftDt.isWords || !rightDt.isBytes) {
|
||||||
// determine common datatype and add typecast as required to make left and right equal types
|
// determine common datatype and add typecast as required to make left and right equal types
|
||||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right)
|
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right)
|
||||||
if(toFix!=null) {
|
if(toFix!=null) {
|
||||||
@ -153,6 +164,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -375,6 +387,21 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> {
|
||||||
|
if((parent as When).condition.inferType(program).isWords) {
|
||||||
|
val values = whenChoice.values
|
||||||
|
values?.toTypedArray()?.withIndex()?.forEach { (index, value) ->
|
||||||
|
val num = value.constValue(program)
|
||||||
|
if(num!=null && num.type in ByteDatatypes) {
|
||||||
|
val wordNum = NumericLiteral(if(num.type==DataType.UBYTE) DataType.UWORD else DataType.WORD, num.number, num.position)
|
||||||
|
wordNum.parent = num.parent
|
||||||
|
values[index] = wordNum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
private fun addTypecastOrCastedValueModification(
|
private fun addTypecastOrCastedValueModification(
|
||||||
modifications: MutableList<IAstModification>,
|
modifications: MutableList<IAstModification>,
|
||||||
expressionToCast: Expression,
|
expressionToCast: Expression,
|
||||||
|
@ -3,7 +3,7 @@ package prog8tests
|
|||||||
import io.kotest.core.config.AbstractProjectConfig
|
import io.kotest.core.config.AbstractProjectConfig
|
||||||
|
|
||||||
object ProjectConfig : AbstractProjectConfig() {
|
object ProjectConfig : AbstractProjectConfig() {
|
||||||
override val parallelism = 2 // max(2, Runtime.getRuntime().availableProcessors() / 2)
|
override val parallelism = kotlin.math.max(1, Runtime.getRuntime().availableProcessors() / 2)
|
||||||
// override fun listeners() = listOf(SystemOutToNullListener)
|
// override fun listeners() = listOf(SystemOutToNullListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,4 +96,53 @@ class TestAstChecks: FunSpec({
|
|||||||
"""
|
"""
|
||||||
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("const is not allowed on arrays") {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const ubyte[5] a = 5
|
||||||
|
a[2]=42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors)
|
||||||
|
errors.errors.size shouldBe 1
|
||||||
|
errors.warnings.size shouldBe 0
|
||||||
|
errors.errors[0] shouldContain "const modifier can only be used"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("array indexing is not allowed on a memory mapped variable") {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
&ubyte a = 10000
|
||||||
|
uword z = 500
|
||||||
|
a[4] = (z % 3) as ubyte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors)
|
||||||
|
errors.errors.size shouldBe 1
|
||||||
|
errors.warnings.size shouldBe 0
|
||||||
|
errors.errors[0] shouldContain "indexing requires"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("array decl with expression as size can be initialized with a single value") {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const ubyte n = 40
|
||||||
|
const ubyte half = n / 2
|
||||||
|
ubyte[half] @shared a = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors) shouldNotBe null
|
||||||
|
errors.errors.size shouldBe 0
|
||||||
|
errors.warnings.size shouldBe 0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
|||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
varsHigh = false,
|
varsHigh = false,
|
||||||
|
useNewExprCode = false,
|
||||||
compilationTarget = target.name,
|
compilationTarget = target.name,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
symbolDefs = emptyMap(),
|
symbolDefs = emptyMap(),
|
||||||
|
@ -50,6 +50,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
|||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
varsHigh = false,
|
varsHigh = false,
|
||||||
|
useNewExprCode = false,
|
||||||
compilationTarget = Cx16Target.NAME,
|
compilationTarget = Cx16Target.NAME,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
symbolDefs = emptyMap(),
|
symbolDefs = emptyMap(),
|
||||||
|
@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
|
|||||||
<INITGLOBALS>
|
<INITGLOBALS>
|
||||||
</INITGLOBALS>
|
</INITGLOBALS>
|
||||||
|
|
||||||
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
|
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
|
||||||
</BLOCK>
|
</BLOCK>
|
||||||
</PROGRAM>
|
</PROGRAM>
|
||||||
""")
|
""")
|
||||||
|
@ -27,7 +27,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
fun wrapWithProgram(statements: List<Statement>): Program {
|
fun wrapWithProgram(statements: List<Statement>): Program {
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements.toMutableList(), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements.toMutableList(), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
program.addModule(module)
|
program.addModule(module)
|
||||||
return program
|
return program
|
||||||
@ -153,7 +153,7 @@ class TestMemory: FunSpec({
|
|||||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
@ -165,7 +165,7 @@ class TestMemory: FunSpec({
|
|||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
@ -177,7 +177,7 @@ class TestMemory: FunSpec({
|
|||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
@ -189,7 +189,7 @@ class TestMemory: FunSpec({
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
@ -202,7 +202,7 @@ class TestMemory: FunSpec({
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
@ -215,7 +215,7 @@ class TestMemory: FunSpec({
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
|
@ -334,6 +334,7 @@ class TestOptimization: FunSpec({
|
|||||||
uword yy ; to be removed
|
uword yy ; to be removed
|
||||||
yy=99 ; to be removed
|
yy=99 ; to be removed
|
||||||
cx16.r0 = 0
|
cx16.r0 = 0
|
||||||
|
cx16.r0++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
@ -343,7 +344,7 @@ class TestOptimization: FunSpec({
|
|||||||
ifstmt.truepart.statements.size shouldBe 1
|
ifstmt.truepart.statements.size shouldBe 1
|
||||||
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||||
val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine
|
val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine
|
||||||
func2.statements.size shouldBe 1
|
func2.statements.size shouldBe 2
|
||||||
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,6 +764,20 @@ main {
|
|||||||
sub start() {
|
sub start() {
|
||||||
sort(cards)
|
sort(cards)
|
||||||
}
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = false) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("no string error when inlining") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
test()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub test() {
|
||||||
|
cx16.r0 = "abc"
|
||||||
|
}
|
||||||
}"""
|
}"""
|
||||||
compileText(C64Target(), true, text, writeAssembly = false) shouldNotBe null
|
compileText(C64Target(), true, text, writeAssembly = false) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,8 @@ class TestScoping: FunSpec({
|
|||||||
val errors= ErrorReporterForTests()
|
val errors= ErrorReporterForTests()
|
||||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||||
errors.errors.size shouldBe 1
|
errors.errors.size shouldBe 1
|
||||||
errors.errors[0] shouldContain "undefined symbol: routine2"
|
errors.errors[0] shouldContain "undefined"
|
||||||
|
errors.errors[0] shouldContain "routine2"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("good subroutine calls with qualified names (from root)") {
|
test("good subroutine calls with qualified names (from root)") {
|
||||||
@ -201,10 +202,14 @@ class TestScoping: FunSpec({
|
|||||||
val errors= ErrorReporterForTests()
|
val errors= ErrorReporterForTests()
|
||||||
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
||||||
errors.errors.size shouldBe 4
|
errors.errors.size shouldBe 4
|
||||||
errors.errors[0] shouldContain "undefined symbol: start.routine2"
|
errors.errors[0] shouldContain "undefined"
|
||||||
errors.errors[1] shouldContain "undefined symbol: wrong.start.routine2"
|
errors.errors[0] shouldContain "start.routine2"
|
||||||
errors.errors[2] shouldContain "undefined symbol: start.routine2"
|
errors.errors[1] shouldContain "undefined"
|
||||||
errors.errors[3] shouldContain "undefined symbol: wrong.start.routine2"
|
errors.errors[1] shouldContain "wrong.start.routine2"
|
||||||
|
errors.errors[2] shouldContain "undefined"
|
||||||
|
errors.errors[2] shouldContain "start.routine2"
|
||||||
|
errors.errors[3] shouldContain "undefined"
|
||||||
|
errors.errors[3] shouldContain "wrong.start.routine2"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("good variables without qualified names") {
|
test("good variables without qualified names") {
|
||||||
@ -408,7 +413,16 @@ class TestScoping: FunSpec({
|
|||||||
"""
|
"""
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||||
println(errors.errors)
|
/*
|
||||||
|
There are 4 errors and 3 warnings.
|
||||||
|
ERROR name conflict 'start', also defined...
|
||||||
|
ERROR name conflict 'var1Warn', also defined...
|
||||||
|
ERROR name conflict 'main', also defined...
|
||||||
|
ERROR name conflict 'internalOk', also defined...
|
||||||
|
WARN name 'var1Warn' shadows occurrence at...
|
||||||
|
WARN name 'var1Warn' shadows occurrence at...
|
||||||
|
WARN name 'var1Warn' shadows occurrence at...
|
||||||
|
*/
|
||||||
errors.warnings.size shouldBe 3
|
errors.warnings.size shouldBe 3
|
||||||
errors.warnings[0] shouldContain "var1Warn"
|
errors.warnings[0] shouldContain "var1Warn"
|
||||||
errors.warnings[0] shouldContain "shadows"
|
errors.warnings[0] shouldContain "shadows"
|
||||||
@ -419,7 +433,7 @@ class TestScoping: FunSpec({
|
|||||||
errors.warnings[2] shouldContain "var1Warn"
|
errors.warnings[2] shouldContain "var1Warn"
|
||||||
errors.warnings[2] shouldContain "shadows"
|
errors.warnings[2] shouldContain "shadows"
|
||||||
errors.warnings[2] shouldContain "line 3"
|
errors.warnings[2] shouldContain "line 3"
|
||||||
errors.errors.size shouldBe 5
|
errors.errors.size shouldBe 4
|
||||||
errors.errors[0] shouldContain "name conflict"
|
errors.errors[0] shouldContain "name conflict"
|
||||||
errors.errors[0] shouldContain "start"
|
errors.errors[0] shouldContain "start"
|
||||||
errors.errors[0] shouldContain "line 5"
|
errors.errors[0] shouldContain "line 5"
|
||||||
@ -430,10 +444,7 @@ class TestScoping: FunSpec({
|
|||||||
errors.errors[2] shouldContain "main"
|
errors.errors[2] shouldContain "main"
|
||||||
errors.errors[2] shouldContain "line 2"
|
errors.errors[2] shouldContain "line 2"
|
||||||
errors.errors[3] shouldContain "name conflict"
|
errors.errors[3] shouldContain "name conflict"
|
||||||
errors.errors[3] shouldContain "start"
|
errors.errors[3] shouldContain "internalOk"
|
||||||
errors.errors[3] shouldContain "line 5"
|
errors.errors[3] shouldContain "line 11"
|
||||||
errors.errors[4] shouldContain "name conflict"
|
|
||||||
errors.errors[4] shouldContain "internalOk"
|
|
||||||
errors.errors[4] shouldContain "line 11"
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1001,4 +1001,19 @@ main {
|
|||||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||||
compileText(VMTarget(), true, text, writeAssembly = true) shouldNotBe null
|
compileText(VMTarget(), true, text, writeAssembly = true) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("byte when choices silently converted to word for convenience") {
|
||||||
|
var text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword z = 3
|
||||||
|
when z {
|
||||||
|
1-> z++
|
||||||
|
2-> z++
|
||||||
|
else -> z++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -66,7 +66,7 @@ class TestIntermediateAst: FunSpec({
|
|||||||
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
|
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
|
||||||
fcall.void shouldBe false
|
fcall.void shouldBe false
|
||||||
fcall.type shouldBe DataType.UBYTE
|
fcall.type shouldBe DataType.UBYTE
|
||||||
printAst(ast, ::println)
|
printAst(ast, false, ::println)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
@ -311,7 +311,7 @@ class TestProg8Parser: FunSpec( {
|
|||||||
sub start() {
|
sub start() {
|
||||||
ubyte foo = 42
|
ubyte foo = 42
|
||||||
ubyte bar
|
ubyte bar
|
||||||
when (foo) {
|
when foo {
|
||||||
23 -> bar = 'x'
|
23 -> bar = 'x'
|
||||||
42 -> bar = 'y'
|
42 -> bar = 'y'
|
||||||
else -> bar = 'z'
|
else -> bar = 'z'
|
||||||
|
@ -3,6 +3,7 @@ package prog8tests.ast
|
|||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -12,6 +13,7 @@ import prog8.ast.statements.VarDecl
|
|||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
class TestVariousCompilerAst: FunSpec({
|
class TestVariousCompilerAst: FunSpec({
|
||||||
@ -132,7 +134,7 @@ main {
|
|||||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
test("bitshift left of const byte converted to word") {
|
test("bitshift left of const byte not converted to word") {
|
||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
@ -201,5 +203,59 @@ main {
|
|||||||
value2.left shouldBe instanceOf<ContainmentCheck>()
|
value2.left shouldBe instanceOf<ContainmentCheck>()
|
||||||
(value2.right as NumericLiteral).number shouldBe 0.0
|
(value2.right as NumericLiteral).number shouldBe 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("const pointer variable indexing works") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const uword pointer=$1000
|
||||||
|
cx16.r0L = pointer[2]
|
||||||
|
pointer[2] = cx16.r0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("unroll good") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
unroll 200 {
|
||||||
|
cx16.r0++
|
||||||
|
poke(2000,2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldNotBe null
|
||||||
|
errors.warnings.size shouldBe 1
|
||||||
|
errors.warnings[0] shouldContain "large number of unrolls"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("unroll bad") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
repeat {
|
||||||
|
unroll 80 {
|
||||||
|
cx16.r0++
|
||||||
|
when cx16.r0 {
|
||||||
|
1 -> cx16.r0++
|
||||||
|
else -> cx16.r0++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 2
|
||||||
|
errors.errors[0] shouldContain "invalid statement in unroll loop"
|
||||||
|
errors.errors[1] shouldContain "invalid statement in unroll loop"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class TestAsmGenSymbols: StringSpec({
|
|||||||
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
|
|
||||||
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
||||||
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements, Position.DUMMY)
|
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
|
||||||
val labelInBlock = Label("label_outside", Position.DUMMY)
|
val labelInBlock = Label("label_outside", Position.DUMMY)
|
||||||
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, Position.DUMMY)
|
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, Position.DUMMY)
|
||||||
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
||||||
|
@ -80,4 +80,19 @@ class TestVariables: FunSpec({
|
|||||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("initialization of boolean array with single value") {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
bool[10] sieve0 = false
|
||||||
|
bool[10] sieve1 = true
|
||||||
|
bool[10] sieve2 = 42
|
||||||
|
sieve0[0] = true
|
||||||
|
sieve1[0] = true
|
||||||
|
sieve2[0] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -4,6 +4,7 @@ import io.kotest.core.spec.style.FunSpec
|
|||||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.string.shouldStartWith
|
import io.kotest.matchers.string.shouldStartWith
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import prog8.code.ast.PtArrayIndexer
|
import prog8.code.ast.PtArrayIndexer
|
||||||
@ -11,6 +12,8 @@ import prog8.code.ast.PtAssignment
|
|||||||
import prog8.code.ast.PtVariable
|
import prog8.code.ast.PtVariable
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
class TestVariousCodeGen: FunSpec({
|
class TestVariousCodeGen: FunSpec({
|
||||||
@ -91,4 +94,75 @@ main {
|
|||||||
assign.target.identifier!!.name shouldBe "cx16.r0"
|
assign.target.identifier!!.name shouldBe "cx16.r0"
|
||||||
assign.value shouldBe instanceOf<PtArrayIndexer>()
|
assign.value shouldBe instanceOf<PtArrayIndexer>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("peek and poke argument types") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword[3] arr
|
||||||
|
ubyte i = 42
|
||||||
|
uword ww = peekw(arr[i])
|
||||||
|
ubyte xx = peek(arr[i])
|
||||||
|
xx = @(arr[i])
|
||||||
|
|
||||||
|
@(arr[i]) = 42
|
||||||
|
poke(arr[i], 42)
|
||||||
|
pokew(arr[i], 4242)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("assigning memory byte into arrays works") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword factor1
|
||||||
|
ubyte[3] bytearray
|
||||||
|
uword[3] wordarray
|
||||||
|
@(factor1) = bytearray[0]
|
||||||
|
bytearray[0] = @(factor1)
|
||||||
|
@(factor1) = lsb(wordarray[0])
|
||||||
|
wordarray[0] = @(factor1)
|
||||||
|
@(5000) = bytearray[0]
|
||||||
|
@(5000) = lsb(wordarray[0])
|
||||||
|
bytearray[0] = @(5000)
|
||||||
|
wordarray[0] = @(5000)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("reading memory from unknown var gives proper error") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
cx16.r0L = @(doesnotexist)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = true, errors = errors)
|
||||||
|
errors.errors.size shouldBe 1
|
||||||
|
errors.errors[0] shouldContain "undefined symbol: doesnotexist"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("shifting by word value is ok") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte c = 1
|
||||||
|
@(15000 + c<<${'$'}0003) = 42
|
||||||
|
@(15000 + (c<<${'$'}0003)) = 42
|
||||||
|
@(15000 + c*${'$'}0008) = 42 ; *8 becomes a shift after opt
|
||||||
|
|
||||||
|
uword @shared qq = 15000 + c<<${'$'}0003
|
||||||
|
qq = 15000 + (c<<${'$'}0003)
|
||||||
|
qq = 16000 + c*${'$'}0008
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
||||||
|
compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
||||||
|
// no newexpr for IR targets: compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
||||||
|
}
|
||||||
})
|
})
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user