mirror of
https://github.com/irmen/prog8.git
synced 2025-06-20 06:23:34 +00:00
Compare commits
167 Commits
Author | SHA1 | Date | |
---|---|---|---|
0aa0ec5abd | |||
b6eef3612f | |||
666d62dd7a | |||
44ee4b989f | |||
18790d867c | |||
d6b8936376 | |||
4d840c7db8 | |||
4d2b21816d | |||
2d34fdd28f | |||
68abda1219 | |||
f778f08f76 | |||
ac1bd2fb7b | |||
4b7b1379d9 | |||
e560e2ab3f | |||
1e441c2ddf | |||
93ce74eeb1 | |||
f718f4251b | |||
4644c9b621 | |||
197081f10d | |||
00b717cde8 | |||
34aa917ca4 | |||
a38ddcb364 | |||
5b9576df4e | |||
310219e5d7 | |||
a0deb463c9 | |||
90ddec2ad8 | |||
f6b03d5a78 | |||
f531daa872 | |||
046dceb5c2 | |||
dcc1f00048 | |||
05f935b598 | |||
f2d27403c5 | |||
473efbe67a | |||
aeabf0f324 | |||
80ab552ad8 | |||
7d4695c5b2 | |||
5189eaca36 | |||
cfb31377fc | |||
a07c52e112 | |||
8e1071aa89 | |||
7cb9a6ba60 | |||
350dc731f1 | |||
f690f58bd4 | |||
4bc65e9ef7 | |||
2d600da8b6 | |||
35af53828a | |||
10ddd5b127 | |||
f46e131f18 | |||
feb5c8be95 | |||
edf12bec71 | |||
ff1fc28287 | |||
314398ba4c | |||
840331347b | |||
6181b12ab8 | |||
68da661edc | |||
88cbb6913d | |||
7a26646e1b | |||
92eb3b0bf6 | |||
fb63434eee | |||
97f90d9684 | |||
f91786367f | |||
6a57337a68 | |||
211e2bb37a | |||
d2d08bf143 | |||
8acb37b6c2 | |||
81b3d2db4f | |||
9633c0b07a | |||
1dfa8ee7d8 | |||
1163543a98 | |||
bdb7de34be | |||
9500fc11ac | |||
65daf29acd | |||
298b25cf7d | |||
41f4e22a17 | |||
288c57c144 | |||
7ff8923569 | |||
b41779bd02 | |||
beea6bc794 | |||
fee58e98c5 | |||
c51c1da618 | |||
ea2812f50f | |||
3ec05709d5 | |||
4bdac7404a | |||
cc41218d37 | |||
4b336b1853 | |||
e1c77ce236 | |||
064d412ec8 | |||
7fff4f249d | |||
7a3745f642 | |||
f8658f6afa | |||
223b725a10 | |||
25aad8d7be | |||
b2c9b7635d | |||
24d13dd120 | |||
965340ff90 | |||
8e36fe6bef | |||
2eb41a8caf | |||
fb989ae62f | |||
7901ec2a64 | |||
f675dbc726 | |||
2ad4fdbbb9 | |||
97cb0cbd08 | |||
4ca0805de1 | |||
4b358abbb7 | |||
dc82a0fc16 | |||
435d6f6f3f | |||
ef92451d1a | |||
06184bdcb1 | |||
af98d01053 | |||
bb1cda0916 | |||
a6d0ea347c | |||
0fcd57192b | |||
a6ffa5738b | |||
c75bd97537 | |||
eea09f4de5 | |||
5656ec11d3 | |||
eb53e44cb0 | |||
69f3106062 | |||
8ab99f6129 | |||
53a3c59a91 | |||
df36983049 | |||
bda016bb3b | |||
cc174b7b85 | |||
bf9d120081 | |||
775c85fc18 | |||
5a756aaed9 | |||
dca092fd7c | |||
c6e92ecac4 | |||
93008ff605 | |||
43c7b935df | |||
8f9a0a244a | |||
fd13bd864e | |||
710f27afa9 | |||
f537793b0b | |||
f7183e38ee | |||
0a65dfdd10 | |||
3075578245 | |||
b042b7705e | |||
d56eb397f9 | |||
3054a1d32d | |||
0a3cd652b0 | |||
f70b914779 | |||
46ca0ac10d | |||
031f647952 | |||
8f1c86f550 | |||
926fdecd13 | |||
af2ca7a67e | |||
9e3e2ff81a | |||
a9fe6472d9 | |||
a862a81480 | |||
dbb92881a1 | |||
10bf7f5d07 | |||
1e61d84fd1 | |||
8618ba1b60 | |||
3c8c44155d | |||
2002412026 | |||
7f69517fd4 | |||
851f8645b5 | |||
c40cfaa388 | |||
0349d1d57c | |||
53049c02ee | |||
73a3a61729 | |||
5fe6aa2800 | |||
c7eafd7c79 | |||
10b5fb5d72 | |||
c4eaa944e2 | |||
a735939d1e |
22
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
22
.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.1.0" />
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.0.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$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.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.10/kotlin-stdlib-1.6.10.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.10/kotlin-stdlib-jdk7-1.6.10.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.1.0/kotest-assertions-shared-jvm-5.1.0.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/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.11/java-diff-utils-4.11.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.0/kotlinx-coroutines-jdk8-1.6.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-reflect/1.6.10/kotlin-reflect-1.6.10.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.0/kotlinx-coroutines-core-jvm-1.6.0.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/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.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-common-jvm/5.1.0/kotest-common-jvm-5.1.0.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-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.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 />
|
||||||
|
30
.idea/libraries/io_kotest_property_jvm.xml
generated
30
.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.1.0" />
|
<properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.1.0/kotest-property-jvm-5.1.0.jar!/" />
|
<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-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.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/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.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.11/java-diff-utils-4.11.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.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/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$/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/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
60
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
60
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
@ -1,39 +1,39 @@
|
|||||||
<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.1.0" />
|
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.1.0/kotest-runner-junit5-jvm-5.1.0.jar!/" />
|
<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-framework-api-jvm/5.1.0/kotest-framework-api-jvm-5.1.0.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$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.0/kotlinx-coroutines-test-jvm-1.6.0.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.1.0/kotest-assertions-shared-jvm-5.1.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$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.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/kotest/kotest-framework-engine-jvm/5.1.0/kotest-framework-engine-jvm-5.1.0.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/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.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/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.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/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.0/kotlinx-coroutines-debug-1.6.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$/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$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.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$/io/kotest/kotest-framework-discovery-jvm/5.1.0/kotest-framework-discovery-jvm-5.1.0.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$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.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/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.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-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.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$/io/kotest/kotest-extensions-jvm/5.1.0/kotest-extensions-jvm-5.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.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/mockk/mockk/1.12.2/mockk-1.12.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.3/mockk-1.12.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.2/mockk-dsl-jvm-1.12.2.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-dsl/1.12.2/mockk-dsl-1.12.2.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-common/1.12.2/mockk-common-1.12.2.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.12.2/mockk-agent-jvm-1.12.2.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$/io/mockk/mockk-agent-api/1.12.2/mockk-agent-api-1.12.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-common/1.12.2/mockk-agent-common-1.12.2.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$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.5/byte-buddy-1.12.5.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.5/byte-buddy-agent-1.12.5.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$/io/kotest/kotest-framework-concurrency-jvm/5.1.0/kotest-framework-concurrency-jvm-5.1.0.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/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.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/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.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/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!/" />
|
||||||
@ -41,11 +41,11 @@
|
|||||||
<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.7.2/junit-jupiter-api-5.7.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.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.10/kotlin-stdlib-1.6.10.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.10/kotlin-stdlib-jdk7-1.6.10.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-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
12
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
12
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
@ -1,13 +1,13 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14" />
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.14/kotlin-result-jvm-1.1.14.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.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.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.10/kotlin-stdlib-common-1.6.10.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
10
.idea/libraries/takes.xml
generated
10
.idea/libraries/takes.xml
generated
@ -1,11 +1,15 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="takes" type="repository">
|
<library name="takes" type="repository">
|
||||||
<properties maven-id="org.takes:takes:1.19" />
|
<properties maven-id="org.takes:takes:1.20" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.19/takes-1.19.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.42/cactoos-0.42.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.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$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.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$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
8
LICENSE
8
LICENSE
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
This sofware license is for Prog8 the compiler + associated libraries.
|
This sofware license is for Prog8 the compiler + associated library files.
|
||||||
|
|
||||||
Any and all outputs generated by the compiler (intermediary
|
Exception: All output files generated by the compiler (intermediary files
|
||||||
source code and compiled binary programs) are excluded from that
|
and compiled binary programs) are excluded from this; you can do with those
|
||||||
and you can do with them whatever you want.
|
whatever you want.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,10 +16,11 @@ https://prog8.readthedocs.io/
|
|||||||
|
|
||||||
Software license
|
Software license
|
||||||
----------------
|
----------------
|
||||||
GNU GPL 3.0, see file LICENSE
|
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||||
|
|
||||||
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
- The compiler and its libraries are free to use according to the terms of the GNU GPL 3.0
|
||||||
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
- *exception:* the resulting files (intermediate source codes and resulting binary program) created by the compiler
|
||||||
|
are excluded from the GPL and are free to use in whatever way desired, commercially or not.
|
||||||
|
|
||||||
|
|
||||||
What does Prog8 provide?
|
What does Prog8 provide?
|
||||||
|
@ -40,6 +40,20 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
|||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
||||||
|
val vars = mutableListOf<StMemVar>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.MEMVAR)
|
||||||
|
vars.add(child.value as StMemVar)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>) = flat[scopedName]
|
override fun lookup(scopedName: List<String>) = flat[scopedName]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +200,11 @@ class StConstant(name: String, val dt: DataType, val value: Double, position: Po
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) :
|
class StMemVar(name: String,
|
||||||
|
val dt: DataType,
|
||||||
|
val address: UInt,
|
||||||
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
|
position: Position) :
|
||||||
StNode(name, StNodeType.MEMVAR, position) {
|
StNode(name, StNodeType.MEMVAR, position) {
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print("$name dt=$dt address=${address.toHex()}")
|
print("$name dt=$dt address=${address.toHex()}")
|
||||||
@ -194,7 +212,7 @@ class StMemVar(name: String, val dt: DataType, val address: UInt, position: Posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, position: Position) :
|
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
|
||||||
StNode(name, StNodeType.SUBROUTINE, position) {
|
StNode(name, StNodeType.SUBROUTINE, position) {
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print(name)
|
print(name)
|
||||||
@ -202,7 +220,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, position:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StRomSub(name: String, val address: UInt, parameters: List<StSubroutineParameter>, position: Position) :
|
class StRomSub(name: String, val address: UInt, val parameters: List<StSubroutineParameter>, val returnTypes: List<DataType>, position: Position) :
|
||||||
StNode(name, StNodeType.ROMSUB, position) {
|
StNode(name, StNodeType.ROMSUB, position) {
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print("$name address=${address.toHex()}")
|
print("$name address=${address.toHex()}")
|
||||||
|
@ -8,6 +8,21 @@ import kotlin.math.round
|
|||||||
|
|
||||||
|
|
||||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(type==DataType.BOOL)
|
||||||
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
|
if(type==DataType.UNDEFINED) {
|
||||||
|
@Suppress("LeakingThis")
|
||||||
|
when(this) {
|
||||||
|
is PtBuiltinFunctionCall -> { /* void function call */ }
|
||||||
|
is PtFunctionCall -> { /* void function call */ }
|
||||||
|
is PtIdentifier -> { /* non-variable identifier */ }
|
||||||
|
else -> throw IllegalArgumentException("type should be known @$position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print(type)
|
print(type)
|
||||||
}
|
}
|
||||||
@ -73,6 +88,7 @@ class PtBuiltinFunctionCall(val name: String,
|
|||||||
|
|
||||||
|
|
||||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
|
||||||
val left: PtExpression
|
val left: PtExpression
|
||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val right: PtExpression
|
val right: PtExpression
|
||||||
@ -126,10 +142,12 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
|
|||||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if(type==DataType.BOOL)
|
||||||
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
if(type!=DataType.FLOAT) {
|
if(type!=DataType.FLOAT) {
|
||||||
val rounded = round(number)
|
val rounded = round(number)
|
||||||
if (rounded != number)
|
if (rounded != number)
|
||||||
throw IllegalArgumentException("refused rounding of float to avoid loss of precision")
|
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,23 +167,16 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
|
|
||||||
init {
|
|
||||||
if(!void)
|
|
||||||
require(type!=DataType.UNDEFINED)
|
|
||||||
}
|
|
||||||
|
|
||||||
val segments: List<PtExpression>
|
|
||||||
get() = children.map { it as PtExpression }
|
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children.single() as PtExpression
|
get() = children.single() as PtExpression
|
||||||
|
|
||||||
|
init {
|
||||||
|
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||||
|
if(operator !in setOf("+", "-", "~"))
|
||||||
|
throw IllegalArgumentException("invalid prefix operator: $operator")
|
||||||
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print(operator)
|
print(operator)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ class PtAsmSub(
|
|||||||
val address: UInt?,
|
val address: UInt?,
|
||||||
val clobbers: Set<CpuRegister>,
|
val clobbers: Set<CpuRegister>,
|
||||||
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
|
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
|
||||||
|
val returnTypes: List<DataType>,
|
||||||
val retvalRegisters: List<RegisterOrStatusflag>,
|
val retvalRegisters: List<RegisterOrStatusflag>,
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
position: Position
|
position: Position
|
||||||
|
@ -26,7 +26,7 @@ compileTestKotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
// should have no dependencies to other modules
|
// should have no dependencies to other modules
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -20,5 +20,7 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var asmQuiet: Boolean = false,
|
var asmQuiet: Boolean = false,
|
||||||
var asmListfile: Boolean = false,
|
var asmListfile: Boolean = false,
|
||||||
var experimentalCodegen: Boolean = false,
|
var experimentalCodegen: Boolean = false,
|
||||||
var outputDir: Path = Path("")
|
var evalStackBaseAddress: UInt? = null,
|
||||||
|
var outputDir: Path = Path(""),
|
||||||
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
)
|
)
|
||||||
|
@ -6,12 +6,14 @@ enum class DataType {
|
|||||||
UWORD, // pass by value
|
UWORD, // pass by value
|
||||||
WORD, // pass by value
|
WORD, // pass by value
|
||||||
FLOAT, // pass by value
|
FLOAT, // pass by value
|
||||||
|
BOOL, // pass by value
|
||||||
STR, // pass by reference
|
STR, // pass by reference
|
||||||
ARRAY_UB, // pass by reference
|
ARRAY_UB, // pass by reference
|
||||||
ARRAY_B, // pass by reference
|
ARRAY_B, // pass by reference
|
||||||
ARRAY_UW, // pass by reference
|
ARRAY_UW, // pass by reference
|
||||||
ARRAY_W, // pass by reference
|
ARRAY_W, // pass by reference
|
||||||
ARRAY_F, // pass by reference
|
ARRAY_F, // pass by reference
|
||||||
|
ARRAY_BOOL, // pass by reference
|
||||||
UNDEFINED;
|
UNDEFINED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,11 +21,12 @@ enum class DataType {
|
|||||||
*/
|
*/
|
||||||
infix fun isAssignableTo(targetType: DataType) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
|
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
|
||||||
|
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
|
||||||
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
|
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
|
||||||
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
||||||
WORD -> targetType.oneOf(WORD, FLOAT)
|
WORD -> targetType.oneOf(WORD, FLOAT)
|
||||||
FLOAT -> targetType == FLOAT
|
FLOAT -> targetType.oneOf(FLOAT)
|
||||||
STR -> targetType.oneOf(STR, UWORD)
|
STR -> targetType.oneOf(STR, UWORD)
|
||||||
in ArrayDatatypes -> targetType == this
|
in ArrayDatatypes -> targetType == this
|
||||||
else -> false
|
else -> false
|
||||||
@ -53,13 +56,7 @@ enum class DataType {
|
|||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y;
|
Y
|
||||||
|
|
||||||
fun asRegisterOrPair(): RegisterOrPair = when(this) {
|
|
||||||
A -> RegisterOrPair.A
|
|
||||||
X -> RegisterOrPair.X
|
|
||||||
Y -> RegisterOrPair.Y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RegisterOrPair {
|
enum class RegisterOrPair {
|
||||||
@ -115,19 +112,20 @@ enum class BranchCondition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
|
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
|
||||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||||
|
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||||
|
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||||
val IntegerArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W)
|
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
|
||||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||||
val IterableDatatypes = arrayOf(
|
val IterableDatatypes = arrayOf(
|
||||||
DataType.STR,
|
DataType.STR,
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||||
DataType.ARRAY_F
|
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||||
)
|
)
|
||||||
val PassByValueDatatypes = NumericDatatypes
|
val PassByValueDatatypes = NumericDatatypes
|
||||||
val PassByReferenceDatatypes = IterableDatatypes
|
val PassByReferenceDatatypes = IterableDatatypes
|
||||||
@ -137,14 +135,16 @@ val ArrayToElementTypes = mapOf(
|
|||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
DataType.ARRAY_UB to DataType.UBYTE,
|
||||||
DataType.ARRAY_W to DataType.WORD,
|
DataType.ARRAY_W to DataType.WORD,
|
||||||
DataType.ARRAY_UW to DataType.UWORD,
|
DataType.ARRAY_UW to DataType.UWORD,
|
||||||
DataType.ARRAY_F to DataType.FLOAT
|
DataType.ARRAY_F to DataType.FLOAT,
|
||||||
|
DataType.ARRAY_BOOL to DataType.BOOL
|
||||||
)
|
)
|
||||||
val ElementToArrayTypes = mapOf(
|
val ElementToArrayTypes = mapOf(
|
||||||
DataType.BYTE to DataType.ARRAY_B,
|
DataType.BYTE to DataType.ARRAY_B,
|
||||||
DataType.UBYTE to DataType.ARRAY_UB,
|
DataType.UBYTE to DataType.ARRAY_UB,
|
||||||
DataType.WORD to DataType.ARRAY_W,
|
DataType.WORD to DataType.ARRAY_W,
|
||||||
DataType.UWORD to DataType.ARRAY_UW,
|
DataType.UWORD to DataType.ARRAY_UW,
|
||||||
DataType.FLOAT to DataType.ARRAY_F
|
DataType.FLOAT to DataType.ARRAY_F,
|
||||||
|
DataType.BOOL to DataType.ARRAY_BOOL
|
||||||
)
|
)
|
||||||
val Cx16VirtualRegisters = arrayOf(
|
val Cx16VirtualRegisters = arrayOf(
|
||||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||||
|
@ -3,11 +3,6 @@ package prog8.code.core
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
interface IMachineFloat {
|
|
||||||
fun toDouble(): Double
|
|
||||||
fun makeFloatFillAsm(): String
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class CpuType {
|
enum class CpuType {
|
||||||
CPU6502,
|
CPU6502,
|
||||||
CPU65c02,
|
CPU65c02,
|
||||||
@ -18,8 +13,8 @@ interface IMachineDefinition {
|
|||||||
val FLOAT_MAX_NEGATIVE: Double
|
val FLOAT_MAX_NEGATIVE: Double
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
val FLOAT_MEM_SIZE: Int
|
val FLOAT_MEM_SIZE: Int
|
||||||
val ESTACK_LO: UInt
|
var ESTACK_LO: UInt
|
||||||
val ESTACK_HI: UInt
|
var ESTACK_HI: UInt
|
||||||
val PROGRAM_LOAD_ADDRESS : UInt
|
val PROGRAM_LOAD_ADDRESS : UInt
|
||||||
|
|
||||||
val opcodeNames: Set<String>
|
val opcodeNames: Set<String>
|
||||||
@ -27,9 +22,14 @@ interface IMachineDefinition {
|
|||||||
val cpu: CpuType
|
val cpu: CpuType
|
||||||
|
|
||||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||||
fun getFloat(num: Number): IMachineFloat
|
fun getFloatAsmBytes(num: Number): String
|
||||||
|
|
||||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||||
fun isIOAddress(address: UInt): Boolean
|
fun isIOAddress(address: UInt): Boolean
|
||||||
|
fun overrideEvalStack(evalStackBaseAddress: UInt) {
|
||||||
|
require(evalStackBaseAddress and 255u == 0u)
|
||||||
|
ESTACK_LO = evalStackBaseAddress
|
||||||
|
ESTACK_HI = evalStackBaseAddress + 256u
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
|
||||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
val BitwiseOperators = setOf("&", "|", "^")
|
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||||
|
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||||
|
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||||
|
|
||||||
fun invertedComparisonOperator(operator: String) =
|
fun invertedComparisonOperator(operator: String) =
|
||||||
when (operator) {
|
when (operator) {
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
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 {
|
||||||
val path = Path(file).absolute().normalize()
|
return try {
|
||||||
return "file://$path:$line:$startCol:"
|
val path = Path(file).absolute().normalize().toString()
|
||||||
|
"file://$path:$line:$startCol:"
|
||||||
|
} catch(x: InvalidPathException) {
|
||||||
|
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||||
|
"file://$file:$line:$startCol:"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DUMMY = Position("<dummy>", 0, 0, 0)
|
val DUMMY = Position("~dummy~", 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ sealed class SourceCode {
|
|||||||
* Where this [SourceCode] instance came from.
|
* Where this [SourceCode] instance came from.
|
||||||
* This can be one of the following:
|
* This can be one of the following:
|
||||||
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
|
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
|
||||||
* * `$stringSourcePrefix44c56085>` if was created via [String]
|
* * `string:44c56085` if was created via [String]
|
||||||
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
|
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
|
||||||
*/
|
*/
|
||||||
abstract val origin: String
|
abstract val origin: String
|
||||||
@ -55,7 +55,7 @@ sealed class SourceCode {
|
|||||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||||
*/
|
*/
|
||||||
const val libraryFilePrefix = "library:"
|
const val libraryFilePrefix = "library:"
|
||||||
const val stringSourcePrefix = "<String@"
|
const val stringSourcePrefix = "string:"
|
||||||
val curdir: Path = Path(".").toAbsolutePath()
|
val curdir: Path = Path(".").toAbsolutePath()
|
||||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||||
fun isRegularFilesystemPath(pathString: String) =
|
fun isRegularFilesystemPath(pathString: String) =
|
||||||
@ -64,12 +64,12 @@ sealed class SourceCode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn a plain String into a [SourceCode] object.
|
* Turn a plain String into a [SourceCode] object.
|
||||||
* [origin] will be something like `$stringSourcePrefix44c56085>`.
|
* [origin] will be something like `string:44c56085`.
|
||||||
*/
|
*/
|
||||||
class Text(override val text: String): SourceCode() {
|
class Text(override val text: String): SourceCode() {
|
||||||
override val isFromResources = false
|
override val isFromResources = false
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
|
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
|
||||||
override val name = "<unnamed-text>"
|
override val name = "<unnamed-text>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,4 +117,6 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
require(size>0)
|
require(size>0)
|
||||||
return free.containsAll((address until address+size.toUInt()).toList())
|
return free.containsAll((address until address+size.toUInt()).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun allocateCx16VirtualRegisters()
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
|
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
|
||||||
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
|
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = TODO("float from number")
|
override fun getFloatAsmBytes(num: Number) = TODO("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)
|
||||||
|
@ -12,7 +12,6 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
||||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
@ -40,6 +39,14 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val distictFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distictFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() {
|
||||||
|
TODO("Not known if atari can put the virtual regs in ZP")
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,12 +16,12 @@ class C128MachineDefinition: IMachineDefinition {
|
|||||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
|
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
|
||||||
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
|
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
@ -38,6 +38,14 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val distictFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distictFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() {
|
||||||
|
TODO("Not known if C128 can put the virtual regs in ZP")
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,12 +16,12 @@ class C64MachineDefinition: IMachineDefinition {
|
|||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
|
override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
|
||||||
override val ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
|
override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package prog8.code.target.c64
|
package prog8.code.target.c64
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.InternalCompilerException
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.code.core.ZeropageType
|
|
||||||
|
|
||||||
|
|
||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
@ -29,10 +26,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
free.addAll(listOf(
|
free.addAll(listOf(
|
||||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
0x16, 0x17, 0x18, 0x19, 0x1a,
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||||
0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
@ -48,8 +44,8 @@ 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(
|
||||||
0x22, 0x23, 0x24, 0x25,
|
0x03, 0x04, 0x10, 0x11, 0x12,
|
||||||
0x10, 0x11, 0x12, 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,
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
@ -60,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
if(options.zeropage!= ZeropageType.DONTUSE) {
|
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||||
// add the free Zp addresses
|
// add the free Zp addresses
|
||||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||||
} else {
|
} else {
|
||||||
@ -69,6 +65,32 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val distictFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distictFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
|
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||||
|
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
|
||||||
|
allocateCx16VirtualRegisters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() {
|
||||||
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
|
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||||
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
|
for(reg in 0..15) {
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
free.remove((4+reg*2).toUInt())
|
||||||
|
free.remove((5+reg*2).toUInt())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,11 @@
|
|||||||
package prog8.code.target.cbm
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
import prog8.code.core.IMachineFloat
|
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte): IMachineFloat {
|
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
@ -58,7 +57,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toDouble(): Double {
|
fun toDouble(): Double {
|
||||||
if (this == zero) return 0.0
|
if (this == zero) return 0.0
|
||||||
val exp = b0.toInt() - 128
|
val exp = b0.toInt() - 128
|
||||||
val sign = (b1.toInt() and 0x80) > 0
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
@ -67,7 +66,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
|||||||
return if (sign) -result else result
|
return if (sign) -result else result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeFloatFillAsm(): String {
|
fun makeFloatFillAsm(): String {
|
||||||
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||||
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||||
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||||
|
@ -15,12 +15,12 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive
|
override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
|
||||||
override val ESTACK_HI = 0x0500u // $0500-$05ff inclusive
|
override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
listOf("syslib")
|
listOf("syslib")
|
||||||
|
@ -43,10 +43,20 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val distictFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distictFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
allocateCx16VirtualRegisters()
|
||||||
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() {
|
||||||
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
|
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||||
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
@ -56,5 +66,4 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -17,12 +17,12 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||||||
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
|
|
||||||
override val ESTACK_LO = 0u // not actually used
|
override var ESTACK_LO = 0u // not actually used
|
||||||
override val ESTACK_HI = 0u // not actually used
|
override var ESTACK_HI = 0u // not actually used
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage // not actually used
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
|
|
||||||
override fun getFloat(num: Number) = TODO("float from number")
|
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return listOf("syslib")
|
return listOf("syslib")
|
||||||
|
@ -29,7 +29,7 @@ dependencies {
|
|||||||
implementation project(':compilerAst')
|
implementation project(':compilerAst')
|
||||||
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 "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import kotlin.io.path.Path
|
|||||||
import kotlin.io.path.writeLines
|
import kotlin.io.path.writeLines
|
||||||
|
|
||||||
|
|
||||||
internal const val generatedLabelPrefix = "prog8_label_"
|
|
||||||
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
|
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
|
||||||
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
|
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
|
||||||
|
|
||||||
@ -73,19 +72,12 @@ class AsmGen(internal val program: Program,
|
|||||||
|
|
||||||
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||||
|
|
||||||
private var generatedLabelSequenceNumber: Int = 0
|
|
||||||
|
|
||||||
internal fun makeLabel(postfix: String): String {
|
|
||||||
generatedLabelSequenceNumber++
|
|
||||||
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun outputSourceLine(node: Node) {
|
internal fun outputSourceLine(node: Node) {
|
||||||
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
|
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun out(str: String, splitlines: Boolean = true) {
|
internal fun out(str: String, splitlines: Boolean = true) {
|
||||||
val fragment = (if(" | " in str) str.replace("|", "\n") else str).trim('\n')
|
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||||
if (splitlines) {
|
if (splitlines) {
|
||||||
for (line in fragment.splitToSequence('\n')) {
|
for (line in fragment.splitToSequence('\n')) {
|
||||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||||
@ -319,13 +311,12 @@ class AsmGen(internal val program: Program,
|
|||||||
is Subroutine -> programGen.translateSubroutine(stmt)
|
is Subroutine -> programGen.translateSubroutine(stmt)
|
||||||
is InlineAssembly -> translate(stmt)
|
is InlineAssembly -> translate(stmt)
|
||||||
is BuiltinFunctionCallStatement -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
|
is BuiltinFunctionCallStatement -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
|
||||||
is FunctionCallStatement -> functioncallAsmGen.translateFunctionCallStatement(stmt) // TODO try to remove this last usage of FunctionCallStatement node in the codegen.
|
is FunctionCallStatement -> functioncallAsmGen.translateFunctionCallStatement(stmt)
|
||||||
is Assignment -> assignmentAsmGen.translate(stmt)
|
is Assignment -> assignmentAsmGen.translate(stmt)
|
||||||
is Jump -> {
|
is Jump -> {
|
||||||
val (asmLabel, indirect) = getJumpTarget(stmt)
|
val (asmLabel, indirect) = getJumpTarget(stmt)
|
||||||
jmp(asmLabel, indirect)
|
jmp(asmLabel, indirect)
|
||||||
}
|
}
|
||||||
is GoSub -> translate(stmt)
|
|
||||||
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)
|
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)
|
||||||
is Label -> translate(stmt)
|
is Label -> translate(stmt)
|
||||||
is ConditionalBranch -> translate(stmt)
|
is ConditionalBranch -> translate(stmt)
|
||||||
@ -334,7 +325,6 @@ class AsmGen(internal val program: Program,
|
|||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
is When -> translate(stmt)
|
is When -> translate(stmt)
|
||||||
is AnonymousScope -> translate(stmt)
|
is AnonymousScope -> translate(stmt)
|
||||||
is Pipe -> translatePipeExpression(stmt.source, stmt.segments, stmt, isStatement = true, pushResultOnEstack = false)
|
|
||||||
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
|
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
|
||||||
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
||||||
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
||||||
@ -444,27 +434,15 @@ class AsmGen(internal val program: Program,
|
|||||||
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCallExpression(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine): DataType =
|
|
||||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, false, scope)
|
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCallStatement(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine) =
|
|
||||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, true, scope)
|
|
||||||
|
|
||||||
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
|
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
|
||||||
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
|
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
|
||||||
|
|
||||||
internal fun saveXbeforeCall(functionCall: IFunctionCall) =
|
internal fun saveXbeforeCall(functionCall: IFunctionCall) =
|
||||||
functioncallAsmGen.saveXbeforeCall(functionCall)
|
functioncallAsmGen.saveXbeforeCall(functionCall)
|
||||||
|
|
||||||
internal fun saveXbeforeCall(gosub: GoSub) =
|
|
||||||
functioncallAsmGen.saveXbeforeCall(gosub)
|
|
||||||
|
|
||||||
internal fun restoreXafterCall(functionCall: IFunctionCall) =
|
internal fun restoreXafterCall(functionCall: IFunctionCall) =
|
||||||
functioncallAsmGen.restoreXafterCall(functionCall)
|
functioncallAsmGen.restoreXafterCall(functionCall)
|
||||||
|
|
||||||
internal fun restoreXafterCall(gosub: GoSub) =
|
|
||||||
functioncallAsmGen.restoreXafterCall(gosub)
|
|
||||||
|
|
||||||
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
|
||||||
@ -550,7 +528,7 @@ class AsmGen(internal val program: Program,
|
|||||||
if(jump is Jump) {
|
if(jump is Jump) {
|
||||||
translateCompareAndJumpIfTrue(booleanCondition, jump)
|
translateCompareAndJumpIfTrue(booleanCondition, jump)
|
||||||
} else {
|
} else {
|
||||||
val endLabel = makeLabel("if_end")
|
val endLabel = program.makeLabel("if_end")
|
||||||
translateCompareAndJumpIfFalse(booleanCondition, endLabel)
|
translateCompareAndJumpIfFalse(booleanCondition, endLabel)
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
@ -558,8 +536,8 @@ class AsmGen(internal val program: Program,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// both true and else parts
|
// both true and else parts
|
||||||
val elseLabel = makeLabel("if_else")
|
val elseLabel = program.makeLabel("if_else")
|
||||||
val endLabel = makeLabel("if_end")
|
val endLabel = program.makeLabel("if_end")
|
||||||
translateCompareAndJumpIfFalse(booleanCondition, elseLabel)
|
translateCompareAndJumpIfFalse(booleanCondition, elseLabel)
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
jmp(endLabel)
|
jmp(endLabel)
|
||||||
@ -575,7 +553,7 @@ class AsmGen(internal val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: RepeatLoop) {
|
private fun translate(stmt: RepeatLoop) {
|
||||||
val endLabel = makeLabel("repeatend")
|
val endLabel = program.makeLabel("repeatend")
|
||||||
loopEndLabels.push(endLabel)
|
loopEndLabels.push(endLabel)
|
||||||
|
|
||||||
when (stmt.iterations) {
|
when (stmt.iterations) {
|
||||||
@ -628,7 +606,7 @@ class AsmGen(internal val program: Program,
|
|||||||
|
|
||||||
private fun repeatWordCount(count: Int, stmt: RepeatLoop) {
|
private fun repeatWordCount(count: Int, stmt: RepeatLoop) {
|
||||||
require(count in 257..65535)
|
require(count in 257..65535)
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = program.makeLabel("repeat")
|
||||||
if(isTargetCpu(CpuType.CPU65c02)) {
|
if(isTargetCpu(CpuType.CPU65c02)) {
|
||||||
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
|
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
|
||||||
out("""
|
out("""
|
||||||
@ -669,7 +647,7 @@ $repeatLabel""")
|
|||||||
private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) {
|
private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) {
|
||||||
// note: A/Y must have been loaded with the number of iterations!
|
// note: A/Y must have been loaded with the number of iterations!
|
||||||
// no need to explicitly test for 0 iterations as this is done in the countdown logic below
|
// no need to explicitly test for 0 iterations as this is done in the countdown logic below
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = program.makeLabel("repeat")
|
||||||
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
|
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
|
||||||
out("""
|
out("""
|
||||||
sta $counterVar
|
sta $counterVar
|
||||||
@ -690,7 +668,7 @@ $repeatLabel lda $counterVar
|
|||||||
|
|
||||||
private fun repeatByteCount(count: Int, stmt: RepeatLoop) {
|
private fun repeatByteCount(count: Int, stmt: RepeatLoop) {
|
||||||
require(count in 2..256)
|
require(count in 2..256)
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = program.makeLabel("repeat")
|
||||||
if(isTargetCpu(CpuType.CPU65c02)) {
|
if(isTargetCpu(CpuType.CPU65c02)) {
|
||||||
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
|
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
|
||||||
out(" lda #${count and 255} | sta $counterVar")
|
out(" lda #${count and 255} | sta $counterVar")
|
||||||
@ -708,7 +686,7 @@ $repeatLabel lda $counterVar
|
|||||||
|
|
||||||
private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) {
|
private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) {
|
||||||
// note: Y must just have been loaded with the (variable) number of loops to be performed!
|
// note: Y must just have been loaded with the (variable) number of loops to be performed!
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = program.makeLabel("repeat")
|
||||||
if(isTargetCpu(CpuType.CPU65c02)) {
|
if(isTargetCpu(CpuType.CPU65c02)) {
|
||||||
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
|
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
|
||||||
out(" beq $endLabel | sty $counterVar")
|
out(" beq $endLabel | sty $counterVar")
|
||||||
@ -745,7 +723,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val counterVar = makeLabel("counter")
|
val counterVar = program.makeLabel("counter")
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.UBYTE, DataType.UWORD -> {
|
DataType.UBYTE, DataType.UWORD -> {
|
||||||
val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors)
|
val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors)
|
||||||
@ -760,7 +738,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: When) {
|
private fun translate(stmt: When) {
|
||||||
val endLabel = makeLabel("choice_end")
|
val endLabel = program.makeLabel("choice_end")
|
||||||
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
|
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
|
||||||
val conditionDt = stmt.condition.inferType(program)
|
val conditionDt = stmt.condition.inferType(program)
|
||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
@ -771,7 +749,7 @@ $repeatLabel lda $counterVar
|
|||||||
assignExpressionToRegister(stmt.condition, RegisterOrPair.AY)
|
assignExpressionToRegister(stmt.condition, RegisterOrPair.AY)
|
||||||
|
|
||||||
for(choice in stmt.choices) {
|
for(choice in stmt.choices) {
|
||||||
val choiceLabel = makeLabel("choice")
|
val choiceLabel = program.makeLabel("choice")
|
||||||
if(choice.values==null) {
|
if(choice.values==null) {
|
||||||
// the else choice
|
// the else choice
|
||||||
translate(choice.statements)
|
translate(choice.statements)
|
||||||
@ -835,14 +813,14 @@ $repeatLabel lda $counterVar
|
|||||||
} else {
|
} else {
|
||||||
if(stmt.elsepart.isEmpty()) {
|
if(stmt.elsepart.isEmpty()) {
|
||||||
val instruction = branchInstruction(stmt.condition, true)
|
val instruction = branchInstruction(stmt.condition, true)
|
||||||
val elseLabel = makeLabel("branch_else")
|
val elseLabel = program.makeLabel("branch_else")
|
||||||
out(" $instruction $elseLabel")
|
out(" $instruction $elseLabel")
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
out(elseLabel)
|
out(elseLabel)
|
||||||
} else {
|
} else {
|
||||||
val instruction = branchInstruction(stmt.condition, true)
|
val instruction = branchInstruction(stmt.condition, true)
|
||||||
val elseLabel = makeLabel("branch_else")
|
val elseLabel = program.makeLabel("branch_else")
|
||||||
val endLabel = makeLabel("branch_end")
|
val endLabel = program.makeLabel("branch_end")
|
||||||
out(" $instruction $elseLabel")
|
out(" $instruction $elseLabel")
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
jmp(endLabel)
|
jmp(endLabel)
|
||||||
@ -887,18 +865,6 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(gosub: GoSub) {
|
|
||||||
val tgt = gosub.identifier.targetSubroutine(program)
|
|
||||||
if(tgt!=null && tgt.isAsmSubroutine) {
|
|
||||||
// no need to rescue X , this has been taken care of already
|
|
||||||
out(" jsr ${getJumpTarget(gosub)}")
|
|
||||||
} else {
|
|
||||||
saveXbeforeCall(gosub)
|
|
||||||
out(" jsr ${getJumpTarget(gosub)}")
|
|
||||||
restoreXafterCall(gosub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getJumpTarget(jump: Jump): Pair<String, Boolean> {
|
private fun getJumpTarget(jump: Jump): Pair<String, Boolean> {
|
||||||
val ident = jump.identifier
|
val ident = jump.identifier
|
||||||
val label = jump.generatedLabel
|
val label = jump.generatedLabel
|
||||||
@ -918,8 +884,6 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getJumpTarget(gosub: GoSub): String = asmSymbolName(gosub.identifier)
|
|
||||||
|
|
||||||
private fun translate(ret: Return, withRts: Boolean=true) {
|
private fun translate(ret: Return, withRts: Boolean=true) {
|
||||||
ret.value?.let { returnvalue ->
|
ret.value?.let { returnvalue ->
|
||||||
val sub = ret.definingSubroutine!!
|
val sub = ret.definingSubroutine!!
|
||||||
@ -1180,14 +1144,14 @@ $repeatLabel lda $counterVar
|
|||||||
return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel)
|
return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel)
|
||||||
|
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.UBYTE, DataType.UWORD -> {
|
DataType.BOOL, DataType.UBYTE, DataType.UWORD -> {
|
||||||
if(operator=="<") {
|
if(operator=="<") {
|
||||||
out(" jmp $jumpIfFalseLabel")
|
out(" jmp $jumpIfFalseLabel")
|
||||||
return
|
return
|
||||||
} else if(operator==">=") {
|
} else if(operator==">=") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE || dt==DataType.BOOL) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||||
if (left is IFunctionCall && !left.isSimple)
|
if (left is IFunctionCall && !left.isSimple)
|
||||||
out(" cmp #0")
|
out(" cmp #0")
|
||||||
@ -1267,7 +1231,7 @@ $repeatLabel lda $counterVar
|
|||||||
// optimized code if the expression is just an identifier (variable)
|
// optimized code if the expression is just an identifier (variable)
|
||||||
val varname = asmVariableName(variable)
|
val varname = asmVariableName(variable)
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.UBYTE -> when(operator) {
|
DataType.UBYTE, DataType.BOOL -> when(operator) {
|
||||||
"==" -> out(" lda $varname | bne $jumpIfFalseLabel")
|
"==" -> out(" lda $varname | bne $jumpIfFalseLabel")
|
||||||
"!=" -> out(" lda $varname | beq $jumpIfFalseLabel")
|
"!=" -> out(" lda $varname | beq $jumpIfFalseLabel")
|
||||||
">" -> out(" lda $varname | beq $jumpIfFalseLabel")
|
">" -> out(" lda $varname | beq $jumpIfFalseLabel")
|
||||||
@ -2812,123 +2776,6 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translatePipeExpression(source: Expression, segments: List<Expression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
|
|
||||||
|
|
||||||
// TODO more efficient code generation to avoid needless assignments to the temp var
|
|
||||||
|
|
||||||
// the source: an expression (could be anything) producing a value.
|
|
||||||
// one or more segment expressions, all are a IFunctionCall node, and LACKING the implicit first argument.
|
|
||||||
// when 'isStatement'=true, the last segment expression should be treated as a funcion call statement (discarding any result value if there is one)
|
|
||||||
|
|
||||||
val subroutine = scope.definingSubroutine!!
|
|
||||||
var valueDt = source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
|
||||||
var valueSource: AsmAssignSource =
|
|
||||||
if(source is IFunctionCall) {
|
|
||||||
val resultReg = returnRegisterOfFunction(source.target)
|
|
||||||
assignExpressionToRegister(source, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
|
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
|
||||||
} else {
|
|
||||||
AsmAssignSource.fromAstSource(source, program, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the segments (except the last one): function calls taking one or more parameters and producing a value.
|
|
||||||
// directly assign their first argument from the previous call's returnvalue (and take the rest, if any, from the call itself)
|
|
||||||
segments.dropLast(1).forEach {
|
|
||||||
it as IFunctionCall
|
|
||||||
valueDt = translateFunctionCallWithFirstArg(it, valueSource, false, subroutine)
|
|
||||||
val resultReg = returnRegisterOfFunction(it.target)
|
|
||||||
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
|
||||||
}
|
|
||||||
// the last segment: function call taking one or more parameters and optionally producing a result value.
|
|
||||||
val lastCall = segments.last() as IFunctionCall
|
|
||||||
if(isStatement) {
|
|
||||||
translateFunctionCallWithFirstArg(lastCall, valueSource, true, subroutine)
|
|
||||||
} else {
|
|
||||||
valueDt = translateFunctionCallWithFirstArg(lastCall, valueSource, false, subroutine)
|
|
||||||
if(pushResultOnEstack) {
|
|
||||||
when (valueDt) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
out(" sta P8ESTACK_LO,x | dex")
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
out(" jsr floats.push_fac1")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateFunctionCallWithFirstArg(
|
|
||||||
fcall: IFunctionCall,
|
|
||||||
firstArg: AsmAssignSource,
|
|
||||||
isStatement: Boolean,
|
|
||||||
scope: Subroutine
|
|
||||||
): DataType {
|
|
||||||
|
|
||||||
if(fcall.args.isNotEmpty())
|
|
||||||
TODO("deal with additional args (non-unary function): ${fcall.target.nameInSource} (... , ${fcall.args.joinToString(", ")})")
|
|
||||||
|
|
||||||
when(val targetStmt = fcall.target.targetStatement(program)!!) {
|
|
||||||
is BuiltinFunctionPlaceholder -> {
|
|
||||||
return if(isStatement) {
|
|
||||||
translateBuiltinFunctionCallStatement(fcall, firstArg, scope)
|
|
||||||
DataType.UNDEFINED
|
|
||||||
} else {
|
|
||||||
translateBuiltinFunctionCallExpression(fcall, firstArg, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Subroutine -> {
|
|
||||||
val argDt = targetStmt.parameters.single().type
|
|
||||||
if(targetStmt.isAsmSubroutine) {
|
|
||||||
// argument via registers
|
|
||||||
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
|
|
||||||
val assignArgument = AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
translateNormalAssignment(assignArgument)
|
|
||||||
} else {
|
|
||||||
val assignArgument: AsmAssignment =
|
|
||||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(targetStmt)) {
|
|
||||||
// argument goes via registers as optimization
|
|
||||||
val paramReg: RegisterOrPair = when(argDt) {
|
|
||||||
in ByteDatatypes -> RegisterOrPair.A
|
|
||||||
in WordDatatypes -> RegisterOrPair.AY
|
|
||||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
|
||||||
else -> throw AssemblyError("invalid dt")
|
|
||||||
}
|
|
||||||
AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// arg goes via parameter variable
|
|
||||||
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
|
|
||||||
AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
}
|
|
||||||
translateNormalAssignment(assignArgument)
|
|
||||||
}
|
|
||||||
if(targetStmt.shouldSaveX())
|
|
||||||
saveRegisterLocal(CpuRegister.X, scope)
|
|
||||||
out(" jsr ${asmSymbolName(fcall.target)}")
|
|
||||||
if(targetStmt.shouldSaveX())
|
|
||||||
restoreRegisterLocal(CpuRegister.X)
|
|
||||||
return if(isStatement) DataType.UNDEFINED else targetStmt.returntypes.single()
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid call target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
|
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
|
||||||
// note: because A is pushed first so popped last, saving A is often not required here.
|
// note: because A is pushed first so popped last, saving A is often not required here.
|
||||||
val parameter = target.subroutineParameter
|
val parameter = target.subroutineParameter
|
||||||
@ -3006,7 +2853,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, target.datatype, scope, variableAsmName = asmVariableName(target.name))
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.datatype, scope, variableAsmName = asmVariableName(target.scopedName))
|
||||||
if (dt in ByteDatatypes) {
|
if (dt in ByteDatatypes) {
|
||||||
out(" pla")
|
out(" pla")
|
||||||
assignRegister(RegisterOrPair.A, tgt)
|
assignRegister(RegisterOrPair.A, tgt)
|
||||||
@ -3034,6 +2881,9 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun needAsaveForExpr(arg: Expression): Boolean =
|
||||||
|
arg !is NumericLiteral && arg !is IdentifierReference && (arg !is DirectMemoryRead || !arg.isSimple)
|
||||||
|
|
||||||
private val subroutineExtrasCache = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
|
private val subroutineExtrasCache = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
|
||||||
|
|
||||||
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
|
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
|
||||||
|
@ -59,7 +59,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO more assembly optimizations
|
// TODO more assembly peephole optimizations
|
||||||
|
|
||||||
return numberOfOptimizations
|
return numberOfOptimizations
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
|
|||||||
import com.github.michaelbull.result.Ok
|
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.ast.generatedLabelPrefix
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
@ -6,13 +6,11 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.ArrayIndex
|
import prog8.ast.statements.ArrayIndex
|
||||||
import prog8.ast.statements.BuiltinFunctionCallStatement
|
import prog8.ast.statements.BuiltinFunctionCallStatement
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import prog8.compiler.BuiltinFunctions
|
import prog8.compiler.BuiltinFunctions
|
||||||
import prog8.compiler.FSignature
|
import prog8.compiler.FSignature
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
|
||||||
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||||
@ -30,44 +28,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
|
||||||
val name = bfc.target.nameInSource.single()
|
|
||||||
val func = BuiltinFunctions.getValue(name)
|
|
||||||
val argExpression =
|
|
||||||
when(singleArg.kind) {
|
|
||||||
SourceStorageKind.LITERALNUMBER -> singleArg.number!!
|
|
||||||
SourceStorageKind.EXPRESSION -> singleArg.expression!!
|
|
||||||
SourceStorageKind.ARRAY -> singleArg.array!!
|
|
||||||
else -> {
|
|
||||||
// TODO make it so that we can assign efficiently from something else as an expression....namely: register(s)
|
|
||||||
// this is useful in pipe expressions for instance, to skip the use of a temporary variable
|
|
||||||
// but for now, just assign it to a temporary variable and use that as a source
|
|
||||||
// Idea: to do this without having to rewrite every single function in translateFunctioncall(),
|
|
||||||
// hack a special IdentifierReference like "!6502.A/X/Y/AX/AY/XY" to reference a cpu register
|
|
||||||
val tempvar = asmgen.getTempVarName(singleArg.datatype)
|
|
||||||
val assignTempvar = AsmAssignment(
|
|
||||||
singleArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, singleArg.datatype, scope, variableAsmName = asmgen.asmVariableName(tempvar)),
|
|
||||||
false, program.memsizer, Position.DUMMY
|
|
||||||
)
|
|
||||||
assignAsmGen.translateNormalAssignment(assignTempvar)
|
|
||||||
// now use an expression to assign this tempvar
|
|
||||||
val ident = IdentifierReference(tempvar, Position.DUMMY)
|
|
||||||
ident.linkParents(scope)
|
|
||||||
ident
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val argExpressions = mutableListOf(argExpression)
|
|
||||||
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
|
||||||
fcall.linkParents(scope)
|
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
|
||||||
return if(isStatement) {
|
|
||||||
DataType.UNDEFINED
|
|
||||||
} else {
|
|
||||||
builtinFunctionReturnType(func.name).getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if (discardResult && func.pure)
|
if (discardResult && func.pure)
|
||||||
return // can just ignore the whole function call altogether
|
return // can just ignore the whole function call altogether
|
||||||
@ -82,7 +42,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||||
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"swap" -> funcSwap(fcall)
|
|
||||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||||
@ -180,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||||
|
|
||||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
|
||||||
if(bank==null || address==null)
|
|
||||||
throw AssemblyError("callfar (jsrfar) requires constant arguments")
|
|
||||||
|
|
||||||
if(address !in 0xa000..0xbfff)
|
|
||||||
throw AssemblyError("callfar done on address outside of cx16 banked ram")
|
|
||||||
if(bank==0)
|
|
||||||
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
|
||||||
|
|
||||||
val argAddrArg = fcall.args[2]
|
val argAddrArg = fcall.args[2]
|
||||||
|
if(bank==null)
|
||||||
|
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
|
||||||
|
if(fcall.args[1].constValue(program) == null) {
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||||
|
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
|
||||||
|
}
|
||||||
|
|
||||||
if(argAddrArg.constValue(program)?.number == 0.0) {
|
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}""")
|
.byte ${bank.toHex()}""")
|
||||||
} else {
|
} else {
|
||||||
when(argAddrArg) {
|
when(argAddrArg) {
|
||||||
@ -203,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||||
}
|
}
|
||||||
@ -211,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${argAddrArg.number.toHex()}
|
lda ${argAddrArg.number.toHex()}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${argAddrArg.number.toHex()}""")
|
sta ${argAddrArg.number.toHex()}""")
|
||||||
}
|
}
|
||||||
@ -363,9 +321,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
|
||||||
val target =
|
val target =
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
|
||||||
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
allocations.allocateMemorySlab(name, size, align)
|
allocations.allocateMemorySlab(name, size, align)
|
||||||
@ -377,7 +335,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
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, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,6 +470,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
@ -520,6 +479,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
sty (+) + 2
|
sty (+) + 2
|
||||||
+ ror ${'$'}ffff,x ; modified""")
|
+ ror ${'$'}ffff,x ; modified""")
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -613,6 +573,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
@ -621,6 +582,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
sty (+) + 2
|
sty (+) + 2
|
||||||
+ rol ${'$'}ffff,x ; modified""")
|
+ rol ${'$'}ffff,x ; modified""")
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -655,7 +617,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
||||||
|
if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) {
|
||||||
|
if(dt!='b')
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
|
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
|
} else {
|
||||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
|
}
|
||||||
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,7 +648,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
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, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,427 +669,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A | ldy #0")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.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, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun funcSwap(fcall: IFunctionCall) {
|
|
||||||
val first = fcall.args[0]
|
|
||||||
val second = fcall.args[1]
|
|
||||||
|
|
||||||
// optimized simple case: swap two variables
|
|
||||||
if(first is IdentifierReference && second is IdentifierReference) {
|
|
||||||
val firstName = asmgen.asmVariableName(first)
|
|
||||||
val secondName = asmgen.asmVariableName(second)
|
|
||||||
val dt = first.inferType(program)
|
|
||||||
if(dt istype DataType.BYTE || dt istype DataType.UBYTE) {
|
|
||||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(dt istype DataType.WORD || dt istype DataType.UWORD) {
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $firstName
|
|
||||||
lda $secondName
|
|
||||||
sta $firstName
|
|
||||||
sty $secondName
|
|
||||||
ldy $firstName+1
|
|
||||||
lda $secondName+1
|
|
||||||
sta $firstName+1
|
|
||||||
sty $secondName+1
|
|
||||||
""")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(dt istype DataType.FLOAT) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$firstName
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda #>$firstName
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda #<$secondName
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
lda #>$secondName
|
|
||||||
sta P8ZP_SCRATCH_W2+1
|
|
||||||
jsr floats.func_swap_f
|
|
||||||
""")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// optimized simple case: swap two memory locations
|
|
||||||
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
|
||||||
val addr1 = (first.addressExpression as? NumericLiteral)?.number?.toHex()
|
|
||||||
val addr2 = (second.addressExpression as? NumericLiteral)?.number?.toHex()
|
|
||||||
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
|
||||||
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
|
||||||
|
|
||||||
when {
|
|
||||||
addr1!=null && addr2!=null -> {
|
|
||||||
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addr1!=null && name2!=null -> {
|
|
||||||
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name1!=null && addr2 != null -> {
|
|
||||||
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name1!=null && name2!=null -> {
|
|
||||||
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addr1==null && addr2==null && name1==null && name2==null -> {
|
|
||||||
val firstExpr = first.addressExpression as? BinaryExpression
|
|
||||||
val secondExpr = second.addressExpression as? BinaryExpression
|
|
||||||
if(firstExpr!=null && secondExpr!=null) {
|
|
||||||
val pointerVariable = firstExpr.left as? IdentifierReference
|
|
||||||
val firstOffset = firstExpr.right
|
|
||||||
val secondOffset = secondExpr.right
|
|
||||||
if(pointerVariable != null
|
|
||||||
&& pointerVariable isSameAs secondExpr.left
|
|
||||||
&& firstExpr.operator == "+" && secondExpr.operator == "+"
|
|
||||||
&& (firstOffset is NumericLiteral || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
|
||||||
&& (secondOffset is NumericLiteral || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
|
||||||
) {
|
|
||||||
if(firstOffset is NumericLiteral && secondOffset is NumericLiteral) {
|
|
||||||
if(firstOffset!=secondOffset) {
|
|
||||||
swapArrayValues(
|
|
||||||
DataType.UBYTE,
|
|
||||||
asmgen.asmVariableName(pointerVariable), firstOffset,
|
|
||||||
asmgen.asmVariableName(pointerVariable), secondOffset
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if(firstOffset is TypecastExpression && secondOffset is TypecastExpression) {
|
|
||||||
if(firstOffset.type in WordDatatypes && secondOffset.type in WordDatatypes) {
|
|
||||||
val firstOffsetVar = firstOffset.expression as? IdentifierReference
|
|
||||||
val secondOffsetVar = secondOffset.expression as? IdentifierReference
|
|
||||||
if(firstOffsetVar!=null && secondOffsetVar!=null) {
|
|
||||||
if(firstOffsetVar!=secondOffsetVar) {
|
|
||||||
swapArrayValues(
|
|
||||||
DataType.UBYTE,
|
|
||||||
asmgen.asmVariableName(pointerVariable), firstOffsetVar,
|
|
||||||
asmgen.asmVariableName(pointerVariable), secondOffsetVar
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(firstOffset is IdentifierReference || secondOffset is IdentifierReference) {
|
|
||||||
throw AssemblyError("expected a typecast-to-word for index variable at ${firstOffset.position} and/or ${secondOffset.position}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
|
||||||
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
|
|
||||||
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
|
|
||||||
val elementIDt = first.inferType(program)
|
|
||||||
val elementDt = elementIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
|
|
||||||
val firstNum = first.indexer.indexExpr as? NumericLiteral
|
|
||||||
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
|
||||||
val secondNum = second.indexer.indexExpr as? NumericLiteral
|
|
||||||
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
|
||||||
|
|
||||||
if(firstNum!=null && secondNum!=null) {
|
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
|
||||||
return
|
|
||||||
} else if(firstVar!=null && secondVar!=null) {
|
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondVar)
|
|
||||||
return
|
|
||||||
} else if(firstNum!=null && secondVar!=null) {
|
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondVar)
|
|
||||||
return
|
|
||||||
} else if(firstVar!=null && secondNum!=null) {
|
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondNum)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all other types of swap() calls are done via a temporary variable
|
|
||||||
|
|
||||||
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
|
||||||
return when (expr) {
|
|
||||||
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine, variableAsmName = asmgen.asmVariableName(expr))
|
|
||||||
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine, array = expr)
|
|
||||||
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
|
||||||
else -> throw AssemblyError("invalid expression object $expr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when(val datatype: DataType = first.inferType(program).getOr(DataType.UNDEFINED)) {
|
|
||||||
in ByteDatatypes, in WordDatatypes -> {
|
|
||||||
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
|
|
||||||
asmgen.assignExpressionToVariable(second, "P8ZP_SCRATCH_W2", datatype, null)
|
|
||||||
val assignFirst = AsmAssignment(
|
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
|
|
||||||
targetFromExpr(first, datatype),
|
|
||||||
false, program.memsizer, first.position
|
|
||||||
)
|
|
||||||
val assignSecond = AsmAssignment(
|
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
|
|
||||||
targetFromExpr(second, datatype),
|
|
||||||
false, program.memsizer, second.position
|
|
||||||
)
|
|
||||||
asmgen.translateNormalAssignment(assignFirst)
|
|
||||||
asmgen.translateNormalAssignment(assignSecond)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
// via temp variable and FAC1
|
|
||||||
asmgen.assignExpressionTo(first, AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.FLOAT, first.definingSubroutine, "floats.tempvar_swap_float"))
|
|
||||||
asmgen.assignExpressionTo(second, AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, null, register=RegisterOrPair.FAC1))
|
|
||||||
asmgen.translateNormalAssignment(
|
|
||||||
AsmAssignment(
|
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, datatype, register = RegisterOrPair.FAC1),
|
|
||||||
targetFromExpr(first, datatype),
|
|
||||||
false, program.memsizer, first.position
|
|
||||||
)
|
|
||||||
)
|
|
||||||
asmgen.translateNormalAssignment(
|
|
||||||
AsmAssignment(
|
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, "floats.tempvar_swap_float"),
|
|
||||||
targetFromExpr(second, datatype),
|
|
||||||
false, program.memsizer, second.position
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird swap dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexValue2: NumericLiteral) {
|
|
||||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
|
||||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
|
||||||
when(elementDt) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName1+$index1
|
|
||||||
ldy $arrayVarName2+$index2
|
|
||||||
sta $arrayVarName2+$index2
|
|
||||||
sty $arrayVarName1+$index1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName1+$index1
|
|
||||||
ldy $arrayVarName2+$index2
|
|
||||||
sta $arrayVarName2+$index2
|
|
||||||
sty $arrayVarName1+$index1
|
|
||||||
lda $arrayVarName1+$index1+1
|
|
||||||
ldy $arrayVarName2+$index2+1
|
|
||||||
sta $arrayVarName2+$index2+1
|
|
||||||
sty $arrayVarName1+$index1+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<(${arrayVarName1}+$index1)
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda #>(${arrayVarName1}+$index1)
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda #<(${arrayVarName2}+$index2)
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
lda #>(${arrayVarName2}+$index2)
|
|
||||||
sta P8ZP_SCRATCH_W2+1
|
|
||||||
jsr floats.func_swap_f
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid aray elt type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
|
|
||||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
|
||||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
|
||||||
when(elementDt) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
ldx $idxAsmName1
|
|
||||||
ldy $idxAsmName2
|
|
||||||
lda $arrayVarName1,x
|
|
||||||
pha
|
|
||||||
lda $arrayVarName2,y
|
|
||||||
sta $arrayVarName1,x
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2,y
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
asmgen.out("""
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda $idxAsmName1
|
|
||||||
asl a
|
|
||||||
tax
|
|
||||||
lda $idxAsmName2
|
|
||||||
asl a
|
|
||||||
tay
|
|
||||||
lda $arrayVarName1,x
|
|
||||||
pha
|
|
||||||
lda $arrayVarName2,y
|
|
||||||
sta $arrayVarName1,x
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2,y
|
|
||||||
lda $arrayVarName1+1,x
|
|
||||||
pha
|
|
||||||
lda $arrayVarName2+1,y
|
|
||||||
sta $arrayVarName1+1,x
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2+1,y
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #>$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda $idxAsmName1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc $idxAsmName1
|
|
||||||
adc #<$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
+ lda #>$arrayVarName2
|
|
||||||
sta P8ZP_SCRATCH_W2+1
|
|
||||||
lda $idxAsmName2
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc $idxAsmName2
|
|
||||||
adc #<$arrayVarName2
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W2+1
|
|
||||||
+ jsr floats.func_swap_f
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid aray elt type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexName2: IdentifierReference) {
|
|
||||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
|
||||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
|
||||||
when(elementDt) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName1 + $index1
|
|
||||||
pha
|
|
||||||
ldy $idxAsmName2
|
|
||||||
lda $arrayVarName2,y
|
|
||||||
sta $arrayVarName1 + $index1
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName1 + $index1
|
|
||||||
pha
|
|
||||||
lda $idxAsmName2
|
|
||||||
asl a
|
|
||||||
tay
|
|
||||||
lda $arrayVarName2,y
|
|
||||||
sta $arrayVarName1 + $index1
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2,y
|
|
||||||
lda $arrayVarName1 + $index1+1
|
|
||||||
pha
|
|
||||||
lda $arrayVarName2+1,y
|
|
||||||
sta $arrayVarName1 + $index1+1
|
|
||||||
pla
|
|
||||||
sta $arrayVarName2+1,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<(${arrayVarName1}+$index1)
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda #>(${arrayVarName1}+$index1)
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda #>$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda $idxAsmName2
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc $idxAsmName2
|
|
||||||
adc #<$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
+ jsr floats.func_swap_f
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid aray elt type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteral) {
|
|
||||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
|
||||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
|
||||||
when(elementDt) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName2 + $index2
|
|
||||||
pha
|
|
||||||
ldy $idxAsmName1
|
|
||||||
lda $arrayVarName1,y
|
|
||||||
sta $arrayVarName2 + $index2
|
|
||||||
pla
|
|
||||||
sta $arrayVarName1,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName2 + $index2
|
|
||||||
pha
|
|
||||||
lda $idxAsmName1
|
|
||||||
asl a
|
|
||||||
tay
|
|
||||||
lda $arrayVarName1,y
|
|
||||||
sta $arrayVarName2 + $index2
|
|
||||||
pla
|
|
||||||
sta $arrayVarName1,y
|
|
||||||
lda $arrayVarName2 + $index2+1
|
|
||||||
pha
|
|
||||||
lda $arrayVarName1+1,y
|
|
||||||
sta $arrayVarName2 + $index2+1
|
|
||||||
pla
|
|
||||||
sta $arrayVarName1+1,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #>$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda $idxAsmName1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc $idxAsmName1
|
|
||||||
adc #<$arrayVarName1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
+ lda #<(${arrayVarName2}+$index2)
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
lda #>(${arrayVarName2}+$index2)
|
|
||||||
sta P8ZP_SCRATCH_W2+1
|
|
||||||
jsr floats.func_swap_f
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid aray elt type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1144,7 +692,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
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, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1155,7 +703,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr math.randbyte")
|
asmgen.out(" jsr math.randbyte")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"rndw" -> {
|
"rndw" -> {
|
||||||
@ -1163,7 +711,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" jsr prog8_lib.func_rndw_stack")
|
asmgen.out(" jsr prog8_lib.func_rndw_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr math.randword")
|
asmgen.out(" jsr math.randword")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("wrong func")
|
else -> throw AssemblyError("wrong func")
|
||||||
@ -1312,14 +860,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
} else {
|
} else {
|
||||||
val reg = resultRegister ?: RegisterOrPair.AY
|
val reg = resultRegister ?: RegisterOrPair.AY
|
||||||
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteral || fcall.args[0] is IdentifierReference)
|
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||||
if(!needAsave) {
|
if(!needAsave) {
|
||||||
val mr0 = fcall.args[0] as? DirectMemoryRead
|
val mr0 = fcall.args[0] as? DirectMemoryRead
|
||||||
val mr1 = fcall.args[1] as? DirectMemoryRead
|
val mr1 = fcall.args[1] as? DirectMemoryRead
|
||||||
if (mr0 != null)
|
if (mr0 != null)
|
||||||
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
|
needAsave = mr0.addressExpression !is NumericLiteral
|
||||||
if (mr1 != null)
|
if (mr1 != null)
|
||||||
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference)
|
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral)
|
||||||
}
|
}
|
||||||
when(reg) {
|
when(reg) {
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
@ -1536,7 +1084,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
@ -1552,7 +1100,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
||||||
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||||
is PipeExpression -> asmgen.translatePipeExpression(expression.source, expression.segments,
|
|
||||||
expression, isStatement = false, pushResultOnEstack = true )
|
|
||||||
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
||||||
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
@ -138,7 +136,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
private fun translateExpression(typecast: TypecastExpression) {
|
private fun translateExpression(typecast: TypecastExpression) {
|
||||||
translateExpressionInternal(typecast.expression)
|
translateExpressionInternal(typecast.expression)
|
||||||
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE, DataType.BOOL -> {
|
||||||
when(typecast.type) {
|
when(typecast.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
@ -521,12 +519,37 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
if(rightVal!=null && rightVal==2) {
|
if(rightVal!=null && rightVal==2) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
when(leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
DataType.UBYTE -> {
|
||||||
DataType.BYTE -> asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x")
|
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
}
|
||||||
DataType.WORD -> asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
DataType.UWORD -> {
|
||||||
else -> throw AssemblyError("wrong dt")
|
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bpl +
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
+ asl a
|
||||||
|
ror P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_HI+1,x
|
||||||
|
bpl ++
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+ lda P8ESTACK_HI+1,x
|
||||||
|
+ asl a
|
||||||
|
ror P8ESTACK_HI+1,x
|
||||||
|
ror P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -534,8 +557,8 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
in ComparisonOperators -> {
|
in ComparisonOperators -> {
|
||||||
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.constValue(program)?.number
|
||||||
if(rightVal==0)
|
if(rightVal==0.0)
|
||||||
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,6 +585,42 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
|
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
|
||||||
|
if(expr.isSimple) {
|
||||||
|
if(operator=="!=") {
|
||||||
|
when (dt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+ sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
|
||||||
|
asmgen.out("""
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
ora P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+ sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out("""
|
||||||
|
jsr floats.SIGN
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* operator == is not worth it to special case, the code is mostly larger */
|
||||||
|
}
|
||||||
translateExpressionInternal(expr)
|
translateExpressionInternal(expr)
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"==" -> {
|
"==" -> {
|
||||||
@ -666,22 +725,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"not" -> {
|
|
||||||
when(type) {
|
|
||||||
// if reg==0 ->
|
|
||||||
/*
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
*/
|
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
|
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -692,6 +735,25 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
||||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||||
|
|
||||||
|
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
|
||||||
|
if(arrayVarDecl.datatype==DataType.UWORD) {
|
||||||
|
// indexing a pointer var instead of a real array or string
|
||||||
|
if(elementDt !in ByteDatatypes)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
|
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||||
|
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
|
||||||
|
asmgen.out(" lda ($arrayVarName),y")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||||
|
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||||
|
}
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val constIndexNum = arrayExpr.indexer.constIndex()
|
val constIndexNum = arrayExpr.indexer.constIndex()
|
||||||
if(constIndexNum!=null) {
|
if(constIndexNum!=null) {
|
||||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||||
@ -767,9 +829,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||||
"and" -> asmgen.out(" jsr prog8_lib.and_b")
|
|
||||||
"or" -> asmgen.out(" jsr prog8_lib.or_b")
|
|
||||||
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
|
|
||||||
else -> throw AssemblyError("invalid operator $operator")
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -801,9 +860,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||||
"and" -> asmgen.out(" jsr prog8_lib.and_w")
|
|
||||||
"or" -> asmgen.out(" jsr prog8_lib.or_w")
|
|
||||||
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
|
|
||||||
else -> throw AssemblyError("invalid operator $operator")
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -820,7 +876,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||||
"==" -> asmgen.out(" jsr floats.equal_f")
|
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||||
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||||
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
|
||||||
else -> throw AssemblyError("invalid operator $operator")
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,10 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
|
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
val modifiedLabel = program.makeLabel("for_modified")
|
||||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
val modifiedLabel2 = program.makeLabel("for_modifiedb")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||||
|
|
||||||
@ -238,8 +238,8 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val iterableName = asmgen.asmVariableName(ident)
|
val iterableName = asmgen.asmVariableName(ident)
|
||||||
val decl = ident.targetVarDecl(program)!!
|
val decl = ident.targetVarDecl(program)!!
|
||||||
@ -263,7 +263,7 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
val length = decl.arraysize!!.constIndex()!!
|
val length = decl.arraysize!!.constIndex()!!
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = program.makeLabel("for_index")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -299,7 +299,7 @@ $loopLabel sty $indexVar
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
val length = decl.arraysize!!.constIndex()!! * 2
|
val length = decl.arraysize!!.constIndex()!! * 2
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = program.makeLabel("for_index")
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -359,8 +359,8 @@ $loopLabel sty $indexVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not one of the easy cases, generate more complex code...
|
// not one of the easy cases, generate more complex code...
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
@ -471,8 +471,8 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -497,8 +497,8 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -534,8 +534,8 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -561,8 +561,8 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = program.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = program.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
@ -7,7 +7,10 @@ import prog8.ast.expressions.AddressOf
|
|||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.InlineAssembly
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
@ -35,17 +38,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun saveXbeforeCall(gosub: GoSub) {
|
|
||||||
val sub = gosub.identifier.targetSubroutine(program)
|
|
||||||
if(sub?.shouldSaveX()==true) {
|
|
||||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
|
||||||
if(regSaveOnStack)
|
|
||||||
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
|
||||||
else
|
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, gosub.definingSubroutine!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
||||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
if(sub.shouldSaveX()) {
|
if(sub.shouldSaveX()) {
|
||||||
@ -57,17 +49,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreXafterCall(gosub: GoSub) {
|
|
||||||
val sub = gosub.identifier.targetSubroutine(program)
|
|
||||||
if(sub?.shouldSaveX()==true) {
|
|
||||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
|
||||||
if(regSaveOnStack)
|
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
|
||||||
else
|
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
|
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
|
||||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||||
@ -81,11 +62,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
|
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
|
||||||
val subAsmName = asmgen.asmSymbolName(call.target)
|
val subAsmName = asmgen.asmSymbolName(call.target)
|
||||||
|
|
||||||
if(!isExpression && !sub.isAsmSubroutine) {
|
|
||||||
if(!optimizeIntArgsViaRegisters(sub))
|
|
||||||
throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call")
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
if(sub.isAsmSubroutine) {
|
||||||
argumentsViaRegisters(sub, call)
|
argumentsViaRegisters(sub, call)
|
||||||
if (sub.inline && asmgen.options.optimize) {
|
if (sub.inline && asmgen.options.optimize) {
|
||||||
@ -111,10 +87,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
} else {
|
} else {
|
||||||
// 2 byte params, second in Y, first in A
|
// 2 byte params, second in Y, first in A
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
||||||
if(!call.args[1].isSimple)
|
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
||||||
if(!call.args[1].isSimple)
|
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -159,7 +135,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
argOrder.forEach {
|
argOrder.forEach {
|
||||||
val param = callee.parameters[it]
|
val param = callee.parameters[it]
|
||||||
val targetVar = callee.searchAsmParameter(param.name)!!
|
val targetVar = callee.searchParameter(param.name)!!
|
||||||
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
|
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,10 +210,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
} else {
|
} else {
|
||||||
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, program, asmgen, parameter.value.type, sub, register = register)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, 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, program, asmgen)
|
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
|
||||||
}
|
}
|
||||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
if(value is IdentifierReference) {
|
if(value is IdentifierReference) {
|
||||||
|
@ -44,6 +44,15 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
program.allBlocks.forEach { block2asm(it) }
|
program.allBlocks.forEach { block2asm(it) }
|
||||||
|
|
||||||
|
// the global list of all floating point constants for the whole program
|
||||||
|
asmgen.out("; global float constants")
|
||||||
|
for (flt in allocator.globalFloatConsts) {
|
||||||
|
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key)
|
||||||
|
val floatvalue = flt.key
|
||||||
|
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
|
}
|
||||||
|
|
||||||
memorySlabs()
|
memorySlabs()
|
||||||
footer()
|
footer()
|
||||||
}
|
}
|
||||||
@ -70,8 +79,17 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||||
|
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||||
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||||
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||||
|
asmgen.out(".endweak")
|
||||||
|
|
||||||
|
if(options.symbolDefs.isNotEmpty()) {
|
||||||
|
asmgen.out("; -- user supplied symbols on the command line")
|
||||||
|
for((name, value) in options.symbolDefs) {
|
||||||
|
asmgen.out("$name = $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when(options.output) {
|
when(options.output) {
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
@ -127,11 +145,8 @@ internal class ProgramAndVarsGen(
|
|||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
if(options.floats)
|
if(options.floats)
|
||||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||||
asmgen.out(" jsr main.start | lda #4 | sta $01")
|
asmgen.out(" jsr main.start")
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||||
else
|
|
||||||
asmgen.out(" rts")
|
|
||||||
}
|
}
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
asmgen.out(" jsr main.start | lda #31 | sta $01")
|
asmgen.out(" jsr main.start | lda #31 | sta $01")
|
||||||
@ -164,14 +179,6 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
// the global list of all floating point constants for the whole program
|
|
||||||
asmgen.out("; global float constants")
|
|
||||||
for (flt in allocator.globalFloatConsts) {
|
|
||||||
val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
|
|
||||||
val floatvalue = flt.key
|
|
||||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
|
||||||
}
|
|
||||||
|
|
||||||
// program end
|
// program end
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
}
|
}
|
||||||
@ -209,9 +216,9 @@ internal class ProgramAndVarsGen(
|
|||||||
if(!options.dontReinitGlobals) {
|
if(!options.dontReinitGlobals) {
|
||||||
// generate subroutine to initialize block-level (global) variables
|
// generate subroutine to initialize block-level (global) variables
|
||||||
if (initializers.isNotEmpty()) {
|
if (initializers.isNotEmpty()) {
|
||||||
asmgen.out("prog8_init_vars\t.proc\n")
|
asmgen.out("prog8_init_vars\t.block\n")
|
||||||
initializers.forEach { assign -> asmgen.translate(assign) }
|
initializers.forEach { assign -> asmgen.translate(assign) }
|
||||||
asmgen.out(" rts\n .pend")
|
asmgen.out(" rts\n .bend")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,17 +270,27 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
|
|
||||||
|
val asmStartScope: String
|
||||||
|
val asmEndScope: String
|
||||||
|
if(sub.definingBlock.options().contains("force_output")) {
|
||||||
|
asmStartScope = ".block"
|
||||||
|
asmEndScope = ".bend"
|
||||||
|
} else {
|
||||||
|
asmStartScope = ".proc"
|
||||||
|
asmEndScope = ".pend"
|
||||||
|
}
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
if(sub.isAsmSubroutine) {
|
||||||
if(sub.asmAddress!=null)
|
if(sub.asmAddress!=null)
|
||||||
return // already done at the memvars section
|
return // already done at the memvars section
|
||||||
|
|
||||||
// asmsub with most likely just an inline asm in it
|
// asmsub with most likely just an inline asm in it
|
||||||
asmgen.out("${sub.name}\t.proc")
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
sub.statements.forEach { asmgen.translate(it) }
|
sub.statements.forEach { asmgen.translate(it) }
|
||||||
asmgen.out(" .pend\n")
|
asmgen.out(" $asmEndScope\n")
|
||||||
} else {
|
} else {
|
||||||
// regular subroutine
|
// regular subroutine
|
||||||
asmgen.out("${sub.name}\t.proc")
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
|
|
||||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||||
require(scope.type==StNodeType.SUBROUTINE)
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
@ -302,7 +319,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, program, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, 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
|
||||||
@ -310,8 +327,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, program, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
||||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, 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)
|
||||||
}
|
}
|
||||||
@ -350,7 +367,7 @@ internal class ProgramAndVarsGen(
|
|||||||
.map { it.value as StStaticVariable }
|
.map { it.value as StStaticVariable }
|
||||||
nonZpVariables2asm(variables)
|
nonZpVariables2asm(variables)
|
||||||
|
|
||||||
asmgen.out(" .pend\n")
|
asmgen.out(" $asmEndScope\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +449,7 @@ internal class ProgramAndVarsGen(
|
|||||||
val result = mutableListOf<ZpStringWithInitial>()
|
val result = mutableListOf<ZpStringWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
|
val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
|
||||||
if(svar.initialStringValue!=null)
|
if(svar.initialStringValue!=null)
|
||||||
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!))
|
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!))
|
||||||
}
|
}
|
||||||
@ -443,7 +460,7 @@ internal class ProgramAndVarsGen(
|
|||||||
val result = mutableListOf<ZpArrayWithInitial>()
|
val result = mutableListOf<ZpArrayWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
|
val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
|
||||||
if(svar.initialArrayValue!=null)
|
if(svar.initialArrayValue!=null)
|
||||||
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!))
|
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!))
|
||||||
}
|
}
|
||||||
@ -490,7 +507,7 @@ internal class ProgramAndVarsGen(
|
|||||||
if(initialValue==0) {
|
if(initialValue==0) {
|
||||||
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
|
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
|
||||||
} else {
|
} else {
|
||||||
val floatFill = compTarget.machine.getFloat(initialValue).makeFloatFillAsm()
|
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
||||||
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
|
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,7 +566,7 @@ internal class ProgramAndVarsGen(
|
|||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
compTarget.machine.getFloat(it.number!!).makeFloatFillAsm()
|
compTarget.machine.getFloatAsmBytes(it.number!!)
|
||||||
}
|
}
|
||||||
asmgen.out(varname)
|
asmgen.out(varname)
|
||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
@ -589,7 +606,7 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
||||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"")
|
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||||
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
for (chunk in outputBytes.chunked(16))
|
for (chunk in outputBytes.chunked(16))
|
||||||
|
@ -116,9 +116,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println(" number of allocated vars: $numberOfAllocatableVariables")
|
// println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||||
println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||||
println(" zeropage free space: ${zeropage.free.size} bytes")
|
// println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
|
|
||||||
@ -26,7 +29,6 @@ internal enum class SourceStorageKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||||
private val program: Program,
|
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val scope: Subroutine?,
|
val scope: Subroutine?,
|
||||||
@ -37,7 +39,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
val origAstTarget: AssignTarget? = null
|
val origAstTarget: AssignTarget? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
||||||
val asmVarname: String by lazy {
|
val asmVarname: String by lazy {
|
||||||
if (array == null)
|
if (array == null)
|
||||||
@ -68,28 +69,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, program, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
}
|
}
|
||||||
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
|
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
|
||||||
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, asmgen: AsmGen): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, 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, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, 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, register = registers)
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
RegisterOrPair.R2,
|
RegisterOrPair.R2,
|
||||||
@ -105,7 +106,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, 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, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,9 +123,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val expression: Expression? = null
|
val expression: Expression? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
|
||||||
|
|
||||||
val asmVarname: String
|
val asmVarname: String
|
||||||
get() = if(array==null)
|
get() = if(array==null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
@ -132,8 +130,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
|
|
||||||
|
|
||||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||||
val cv = value.constValue(program)
|
val cv = value.constValue(program)
|
||||||
if(cv!=null)
|
if(cv!=null)
|
||||||
|
@ -29,7 +29,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
|
|
||||||
internal fun virtualRegsToVariables(origtarget: AsmAssignTarget): AsmAssignTarget {
|
internal fun virtualRegsToVariables(origtarget: AsmAssignTarget): AsmAssignTarget {
|
||||||
return if(origtarget.kind==TargetStorageKind.REGISTER && origtarget.register in Cx16VirtualRegisters) {
|
return if(origtarget.kind==TargetStorageKind.REGISTER && origtarget.register in Cx16VirtualRegisters) {
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, origtarget.datatype, origtarget.scope,
|
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, origtarget.datatype, origtarget.scope,
|
||||||
variableAsmName = "cx16.${origtarget.register!!.name.lowercase()}", origAstTarget = origtarget.origAstTarget)
|
variableAsmName = "cx16.${origtarget.register!!.name.lowercase()}", origAstTarget = origtarget.origAstTarget)
|
||||||
} else origtarget
|
} else origtarget
|
||||||
}
|
}
|
||||||
@ -72,6 +72,25 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
val value = assign.source.array!!
|
val value = assign.source.array!!
|
||||||
val elementDt = assign.source.datatype
|
val elementDt = assign.source.datatype
|
||||||
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
||||||
|
|
||||||
|
val arrayVarDecl = value.arrayvar.targetVarDecl(program)!!
|
||||||
|
if(arrayVarDecl.datatype==DataType.UWORD) {
|
||||||
|
// indexing a pointer var instead of a real array or string
|
||||||
|
if(elementDt !in ByteDatatypes)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
|
if(value.inferType(program) isnot DataType.UBYTE)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||||
|
if(asmgen.isZpVar(value.arrayvar)) {
|
||||||
|
asmgen.out(" lda ($arrayVarName),y")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||||
|
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||||
|
}
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val constIndex = value.indexer.constIndex()
|
val constIndex = value.indexer.constIndex()
|
||||||
if (constIndex!=null) {
|
if (constIndex!=null) {
|
||||||
// constant array index value
|
// constant array index value
|
||||||
@ -201,9 +220,25 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||||
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
RegisterOrPair.AX -> assignVirtualRegister(assign.target, RegisterOrPair.AX)
|
||||||
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
RegisterOrPair.AY -> assignVirtualRegister(assign.target, RegisterOrPair.AY)
|
||||||
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
RegisterOrPair.XY -> assignVirtualRegister(assign.target, RegisterOrPair.XY)
|
||||||
|
RegisterOrPair.R0 -> assignVirtualRegister(assign.target, RegisterOrPair.R0)
|
||||||
|
RegisterOrPair.R1 -> assignVirtualRegister(assign.target, RegisterOrPair.R1)
|
||||||
|
RegisterOrPair.R2 -> assignVirtualRegister(assign.target, RegisterOrPair.R2)
|
||||||
|
RegisterOrPair.R3 -> assignVirtualRegister(assign.target, RegisterOrPair.R3)
|
||||||
|
RegisterOrPair.R4 -> assignVirtualRegister(assign.target, RegisterOrPair.R4)
|
||||||
|
RegisterOrPair.R5 -> assignVirtualRegister(assign.target, RegisterOrPair.R5)
|
||||||
|
RegisterOrPair.R6 -> assignVirtualRegister(assign.target, RegisterOrPair.R6)
|
||||||
|
RegisterOrPair.R7 -> assignVirtualRegister(assign.target, RegisterOrPair.R7)
|
||||||
|
RegisterOrPair.R8 -> assignVirtualRegister(assign.target, RegisterOrPair.R8)
|
||||||
|
RegisterOrPair.R9 -> assignVirtualRegister(assign.target, RegisterOrPair.R9)
|
||||||
|
RegisterOrPair.R10 -> assignVirtualRegister(assign.target, RegisterOrPair.R10)
|
||||||
|
RegisterOrPair.R11 -> assignVirtualRegister(assign.target, RegisterOrPair.R11)
|
||||||
|
RegisterOrPair.R12 -> assignVirtualRegister(assign.target, RegisterOrPair.R12)
|
||||||
|
RegisterOrPair.R13 -> assignVirtualRegister(assign.target, RegisterOrPair.R13)
|
||||||
|
RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14)
|
||||||
|
RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15)
|
||||||
else -> {
|
else -> {
|
||||||
val sflag = returnValue.second.statusflag
|
val sflag = returnValue.second.statusflag
|
||||||
if(sflag!=null)
|
if(sflag!=null)
|
||||||
@ -263,7 +298,6 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
|
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
|
||||||
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
|
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
|
||||||
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
|
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,35 +305,8 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
containmentCheckIntoA(value)
|
containmentCheckIntoA(value)
|
||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
is PipeExpression -> {
|
|
||||||
asmgen.translatePipeExpression(value.source, value.segments, value, false, false)
|
|
||||||
val resultDt = value.inferType(program)
|
|
||||||
val register =
|
|
||||||
if(resultDt.isBytes) RegisterOrPair.A
|
|
||||||
else if(resultDt.isWords) RegisterOrPair.AY
|
|
||||||
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
|
||||||
else throw AssemblyError("invalid dt")
|
|
||||||
asmgen.assignRegister(register, assign.target)
|
|
||||||
}
|
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(value.operator in ComparisonOperators) {
|
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
||||||
// TODO real optimized code for comparison expressions that yield a boolean result value
|
|
||||||
assignConstantByte(assign.target, 0)
|
|
||||||
val origTarget = assign.target.origAstTarget
|
|
||||||
if(origTarget!=null) {
|
|
||||||
val assignTrue = AnonymousScope(mutableListOf(
|
|
||||||
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
|
||||||
), assign.position)
|
|
||||||
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
|
||||||
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
|
|
||||||
ifelse.linkParents(value)
|
|
||||||
asmgen.translate(ifelse)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// no orig ast assign target so can't use the workaround, so fallback to stack eval
|
|
||||||
fallbackToStackEval(assign)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 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,
|
||||||
// because the code here is the implementation of exactly that...)
|
// because the code here is the implementation of exactly that...)
|
||||||
@ -310,9 +317,400 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
|
||||||
|
when(target.datatype) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
||||||
|
assignRegisterByte(target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
in WordDatatypes -> assignRegisterpairWord(target, register)
|
||||||
|
else -> throw AssemblyError("expected byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
|
if(expr.operator in ComparisonOperators) {
|
||||||
|
if(expr.right.constValue(program)?.number == 0.0) {
|
||||||
|
if(expr.operator == "==" || expr.operator=="!=") {
|
||||||
|
when(assign.target.datatype) {
|
||||||
|
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
|
||||||
|
else -> {
|
||||||
|
// do nothing, this is handled by a type cast.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val origTarget = assign.target.origAstTarget
|
||||||
|
if(origTarget!=null) {
|
||||||
|
assignConstantByte(assign.target, 0)
|
||||||
|
val assignTrue = AnonymousScope(mutableListOf(
|
||||||
|
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||||
|
), assign.position)
|
||||||
|
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||||
|
val ifelse = IfElse(expr.copy(), assignTrue, assignFalse, assign.position)
|
||||||
|
ifelse.linkParents(expr)
|
||||||
|
asmgen.translate(ifelse)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!expr.inferType(program).isInteger)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) {
|
||||||
|
if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes &&
|
||||||
|
expr.left.isSimple && expr.right.isSimple) {
|
||||||
|
if(expr.right is NumericLiteral || expr.right is IdentifierReference)
|
||||||
|
assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
|
||||||
|
else if(expr.left is NumericLiteral || expr.left is IdentifierReference)
|
||||||
|
assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
|
||||||
|
else {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
when (expr.operator) {
|
||||||
|
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
|
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
|
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||||
|
else -> throw AssemblyError("invalid operator")
|
||||||
|
}
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords &&
|
||||||
|
expr.left.isSimple && expr.right.isSimple) {
|
||||||
|
if(expr.right is NumericLiteral || expr.right is IdentifierReference)
|
||||||
|
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
|
||||||
|
else if(expr.left is NumericLiteral || expr.left is IdentifierReference)
|
||||||
|
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
|
||||||
|
else {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine)
|
||||||
|
when (expr.operator) {
|
||||||
|
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
|
||||||
|
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
|
||||||
|
"^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1")
|
||||||
|
else -> throw AssemblyError("invalid operator")
|
||||||
|
}
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if(expr.operator == "==" || expr.operator == "!=") {
|
||||||
|
// expression datatype is BOOL (ubyte) but operands can be anything
|
||||||
|
if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes &&
|
||||||
|
expr.left.isSimple && expr.right.isSimple) {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
if(expr.operator=="==") {
|
||||||
|
asmgen.out("""
|
||||||
|
cmp P8ZP_SCRATCH_B1
|
||||||
|
bne +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
cmp P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
} else if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords &&
|
||||||
|
expr.left.isSimple && expr.right.isSimple) {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
if(expr.operator=="==") {
|
||||||
|
asmgen.out("""
|
||||||
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
bne +
|
||||||
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
bne +
|
||||||
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
lda #0
|
||||||
|
bne ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else if(expr.operator=="+" || expr.operator=="-") {
|
||||||
|
val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") }
|
||||||
|
val left = expr.left
|
||||||
|
val right = expr.right
|
||||||
|
if(dt in ByteDatatypes) {
|
||||||
|
when (right) {
|
||||||
|
is IdentifierReference -> {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||||
|
val symname = asmgen.asmVariableName(right)
|
||||||
|
if(expr.operator=="+")
|
||||||
|
asmgen.out(" clc | adc $symname")
|
||||||
|
else
|
||||||
|
asmgen.out(" sec | sbc $symname")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
is NumericLiteral -> {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||||
|
if(expr.operator=="+")
|
||||||
|
asmgen.out(" clc | adc #${right.number.toHex()}")
|
||||||
|
else
|
||||||
|
asmgen.out(" sec | sbc #${right.number.toHex()}")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
} else if(dt in WordDatatypes) {
|
||||||
|
when (right) {
|
||||||
|
is AddressOf -> {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
val symbol = asmgen.asmVariableName(right.identifier)
|
||||||
|
if(expr.operator=="+")
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc #<$symbol
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
adc #>$symbol
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc #<$symbol
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
sbc #>$symbol
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val symname = asmgen.asmVariableName(right)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
if(expr.operator=="+")
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc $symname
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
adc $symname+1
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $symname
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
sbc $symname+1
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
is NumericLiteral -> {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
if(expr.operator=="+") {
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc #<${right.number.toHex()}
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
adc #>${right.number.toHex()}
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
} else if(expr.operator=="-") {
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc #<${right.number.toHex()}
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
sbc #>${right.number.toHex()}
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
}
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
is TypecastExpression -> {
|
||||||
|
val castedValue = right.expression
|
||||||
|
if(right.type in WordDatatypes && castedValue.inferType(program).isBytes) {
|
||||||
|
if(castedValue is IdentifierReference) {
|
||||||
|
val castedSymname = asmgen.asmVariableName(castedValue)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
if(expr.operator=="+")
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc $castedSymname
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $castedSymname
|
||||||
|
bcs +
|
||||||
|
dey
|
||||||
|
+""")
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||||
|
val operand = when(right) {
|
||||||
|
is NumericLiteral -> "#${right.number.toHex()}"
|
||||||
|
is IdentifierReference -> asmgen.asmSymbolName(right)
|
||||||
|
else -> throw AssemblyError("wrong right operand type")
|
||||||
|
}
|
||||||
|
when (operator) {
|
||||||
|
"&", "and" -> asmgen.out(" and $operand")
|
||||||
|
"|", "or" -> asmgen.out(" ora $operand")
|
||||||
|
"^", "xor" -> asmgen.out(" eor $operand")
|
||||||
|
else -> throw AssemblyError("invalid operator")
|
||||||
|
}
|
||||||
|
assignRegisterByte(target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) {
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY, false)
|
||||||
|
when(right) {
|
||||||
|
is NumericLiteral -> {
|
||||||
|
val number = right.number.toHex()
|
||||||
|
when (operator) {
|
||||||
|
"&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla")
|
||||||
|
"|", "or" -> asmgen.out(" ora #<$number | pha | tya | ora #>$number | tay | pla")
|
||||||
|
"^", "xor" -> asmgen.out(" eor #<$number | pha | tya | eor #>$number | tay | pla")
|
||||||
|
else -> throw AssemblyError("invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val name = asmgen.asmSymbolName(right)
|
||||||
|
when (operator) {
|
||||||
|
"&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla")
|
||||||
|
"|", "or" -> asmgen.out(" ora $name | pha | tya | ora $name+1 | tay | pla")
|
||||||
|
"^", "xor" -> asmgen.out(" eor $name | pha | tya | eor $name+1 | tay | pla")
|
||||||
|
else -> throw AssemblyError("invalid operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("wrong right operand type")
|
||||||
|
}
|
||||||
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
|
when (expr.operator) {
|
||||||
|
"==" -> {
|
||||||
|
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
asmgen.out("""
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
ora P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else->{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||||
|
asmgen.out(" beq + | lda #1")
|
||||||
|
asmgen.out("+")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
|
||||||
|
asmgen.out(" beq + | lda #1")
|
||||||
|
asmgen.out("+")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out(" jsr floats.SIGN")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else->{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun fallbackToStackEval(assign: AsmAssignment) {
|
private fun fallbackToStackEval(assign: AsmAssignment) {
|
||||||
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
|
|
||||||
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.
|
|
||||||
// this routine is called for assigning a binaryexpression value:
|
// this routine is called for assigning a binaryexpression value:
|
||||||
// - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node)
|
// - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node)
|
||||||
// - for all other binary expressions.
|
// - for all other binary expressions.
|
||||||
@ -336,7 +734,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
asmgen.out(" ldy #${stringVal.value.length}")
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
@ -355,14 +753,14 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
asmgen.out(" ldy #${arrayVal.value.size}")
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
||||||
asmgen.out(" ldy #${arrayVal.value.size}")
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
}
|
}
|
||||||
@ -379,7 +777,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
// use subroutine
|
// use subroutine
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
val stringVal = variable.value as StringLiteral
|
val stringVal = variable.value as StringLiteral
|
||||||
asmgen.out(" ldy #${stringVal.value.length}")
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
@ -393,7 +791,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
val arrayVal = variable.value as ArrayLiteral
|
val arrayVal = variable.value as ArrayLiteral
|
||||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
asmgen.out(" ldy #${arrayVal.value.size}")
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
@ -402,7 +800,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
val arrayVal = variable.value as ArrayLiteral
|
val arrayVal = variable.value as ArrayLiteral
|
||||||
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
||||||
asmgen.out(" ldy #${arrayVal.value.size}")
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
return
|
return
|
||||||
@ -513,10 +911,10 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(origTypeCastExpression.type == DataType.UBYTE) {
|
if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) {
|
||||||
val parentTc = origTypeCastExpression.parent as? TypecastExpression
|
val parentTc = origTypeCastExpression.parent as? TypecastExpression
|
||||||
if(parentTc!=null && parentTc.type==DataType.UWORD) {
|
if(parentTc!=null && parentTc.type==DataType.UWORD) {
|
||||||
// typecast something to ubyte and directly back to uword
|
// typecast a word value to ubyte and directly back to uword
|
||||||
// generate code for lsb(value) here instead of the ubyte typecast
|
// generate code for lsb(value) here instead of the ubyte typecast
|
||||||
return assignCastViaLsbFunc(value, target)
|
return assignCastViaLsbFunc(value, target)
|
||||||
}
|
}
|
||||||
@ -566,6 +964,20 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
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)
|
||||||
} else {
|
} else {
|
||||||
|
if(!(valueDt isAssignableTo targetDt)) {
|
||||||
|
if(valueDt in WordDatatypes && targetDt in ByteDatatypes) {
|
||||||
|
// word to byte, just take the lsb
|
||||||
|
return assignCastViaLsbFunc(value, target)
|
||||||
|
} else if(valueDt in WordDatatypes && targetDt in WordDatatypes) {
|
||||||
|
// word to word, just assign
|
||||||
|
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
|
||||||
|
} else if(valueDt in ByteDatatypes && targetDt in ByteDatatypes) {
|
||||||
|
// byte to byte, just assign
|
||||||
|
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
|
||||||
|
}
|
||||||
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
|
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -634,7 +1046,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
when(sourceDt) {
|
when(sourceDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
@ -657,7 +1069,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -687,7 +1099,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.WORD -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -708,7 +1120,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -749,7 +1161,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
when(sourceDt) {
|
when(sourceDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
@ -783,7 +1195,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE -> {
|
||||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -830,7 +1242,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.WORD -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||||
@ -858,7 +1270,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||||
@ -1365,6 +1777,22 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
|
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||||
|
// assigning an indexed pointer var
|
||||||
|
if (target.constArrayIndexValue==0u) {
|
||||||
|
asmgen.out(" lda $sourceName")
|
||||||
|
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||||
|
} else {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||||
|
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||||
|
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||||
|
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
@ -1897,6 +2325,22 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
|
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||||
|
// assigning an indexed pointer var
|
||||||
|
if (target.constArrayIndexValue==0u) {
|
||||||
|
asmgen.out(" lda #0")
|
||||||
|
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||||
|
} else {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||||
|
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||||
|
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||||
|
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val indexValue = target.constArrayIndexValue!!
|
val indexValue = target.constArrayIndexValue!!
|
||||||
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
||||||
@ -1915,10 +2359,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(
|
asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||||
" stz cx16.${
|
|
||||||
target.register.toString().lowercase()
|
|
||||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1940,13 +2381,29 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
|
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||||
|
// assigning an indexed pointer var
|
||||||
|
if (target.constArrayIndexValue==0u) {
|
||||||
|
asmgen.out(" lda #${byte.toHex()}")
|
||||||
|
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||||
|
} else {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||||
|
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val indexValue = target.constArrayIndexValue!!
|
val indexValue = target.constArrayIndexValue!!
|
||||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||||
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
|
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> when(target.register!!) {
|
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||||
@ -2295,7 +2752,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
|
|
||||||
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean) {
|
internal fun assignExpressionToRegister(expr: Expression, 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, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
@ -2305,14 +2762,14 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
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, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, 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, false, program.memsizer, Position.DUMMY)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
|
@ -28,7 +28,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> inplaceNegate(target, type)
|
"-" -> inplaceNegate(target, type)
|
||||||
"~" -> inplaceInvert(target, type)
|
"~" -> inplaceInvert(target, type)
|
||||||
"not" -> inplaceBooleanNot(target, type)
|
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,7 +236,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
|
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
|
||||||
asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, memory.definingSubroutine))
|
asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingSubroutine))
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
||||||
@ -306,7 +305,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
AsmAssignTarget.fromRegisters(
|
AsmAssignTarget.fromRegisters(
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
target.datatype == DataType.BYTE, null,
|
target.datatype == DataType.BYTE, null,
|
||||||
program,
|
|
||||||
asmgen
|
asmgen
|
||||||
)
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
@ -318,7 +316,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
AsmAssignTarget.fromRegisters(
|
AsmAssignTarget.fromRegisters(
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
target.datatype == DataType.WORD, null,
|
target.datatype == DataType.WORD, null,
|
||||||
program,
|
|
||||||
asmgen
|
asmgen
|
||||||
)
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
@ -330,7 +327,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
AsmAssignTarget.fromRegisters(
|
AsmAssignTarget.fromRegisters(
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
true, null,
|
true, null,
|
||||||
program,
|
|
||||||
asmgen
|
asmgen
|
||||||
)
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
@ -391,9 +387,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
@ -428,9 +424,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&", "and" -> asmgen.out(" and $otherName")
|
"&" -> asmgen.out(" and $otherName")
|
||||||
"|", "or" -> asmgen.out(" ora $otherName")
|
"|" -> asmgen.out(" ora $otherName")
|
||||||
"^", "xor" -> asmgen.out(" eor $otherName")
|
"^" -> asmgen.out(" eor $otherName")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
@ -485,17 +481,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" and #$value")
|
asmgen.out(" and #$value")
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|"-> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" ora #$value")
|
asmgen.out(" ora #$value")
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" eor #$value")
|
asmgen.out(" eor #$value")
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
@ -563,15 +559,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
@ -649,9 +645,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||||
"==" -> {
|
"==" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $otherName
|
lda $otherName
|
||||||
@ -740,9 +736,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||||
"==" -> {
|
"==" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
@ -785,15 +781,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc P8ZP_SCRATCH_B1
|
sbc P8ZP_SCRATCH_B1
|
||||||
sta $name""")
|
sta $name""")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
@ -828,11 +824,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
dec $name+1
|
dec $name+1
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
@ -842,7 +838,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
@ -1059,7 +1055,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
when {
|
when {
|
||||||
value == 0 -> {
|
value == 0 -> {
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
@ -1096,7 +1092,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
|
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
when {
|
when {
|
||||||
value == 0 -> {}
|
value == 0 -> {}
|
||||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
||||||
@ -1104,7 +1100,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
|
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
when {
|
when {
|
||||||
value == 0 -> {}
|
value == 0 -> {}
|
||||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
|
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
|
||||||
@ -1268,7 +1264,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
@ -1277,8 +1273,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1349,9 +1345,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1521,7 +1517,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
@ -1531,11 +1527,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
@ -1567,15 +1563,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
remainderVarByWordInAY()
|
remainderVarByWordInAY()
|
||||||
}
|
}
|
||||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||||
"&", "and" -> {
|
"&" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
|
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
@ -1800,136 +1796,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
|
||||||
when (dt) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${target.asmVarname}
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta ${target.asmVarname}""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.MEMORY -> {
|
|
||||||
val mem = target.memory!!
|
|
||||||
when (mem.addressExpression) {
|
|
||||||
is NumericLiteral -> {
|
|
||||||
val addr = (mem.addressExpression as NumericLiteral).number.toHex()
|
|
||||||
asmgen.out("""
|
|
||||||
lda $addr
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta $addr""")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(mem.addressExpression as IdentifierReference)
|
|
||||||
asmgen.out("""
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1""")
|
|
||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
|
|
||||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
|
||||||
asmgen.out("""
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1""")
|
|
||||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) {
|
|
||||||
RegisterOrPair.A -> asmgen.out("""
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1""")
|
|
||||||
RegisterOrPair.X -> asmgen.out("""
|
|
||||||
txa
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
tax""")
|
|
||||||
RegisterOrPair.Y -> asmgen.out("""
|
|
||||||
tya
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
tay""")
|
|
||||||
else -> throw AssemblyError("invalid reg dt for byte not")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack not")
|
|
||||||
else -> throw AssemblyError("no asm gen for in-place not of ubyte ${target.kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${target.asmVarname}
|
|
||||||
ora ${target.asmVarname}+1
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta ${target.asmVarname}
|
|
||||||
lsr a
|
|
||||||
sta ${target.asmVarname}+1""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) {
|
|
||||||
RegisterOrPair.AX -> {
|
|
||||||
asmgen.out("""
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
ora P8ZP_SCRATCH_REG
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
tax
|
|
||||||
beq ++
|
|
||||||
+ lda #1
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
RegisterOrPair.AY -> {
|
|
||||||
asmgen.out("""
|
|
||||||
sty P8ZP_SCRATCH_REG
|
|
||||||
ora P8ZP_SCRATCH_REG
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
tay
|
|
||||||
beq ++
|
|
||||||
+ lda #1
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
RegisterOrPair.XY -> {
|
|
||||||
asmgen.out("""
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
tya
|
|
||||||
ora P8ZP_SCRATCH_REG
|
|
||||||
beq +
|
|
||||||
ldy #0
|
|
||||||
ldx #0
|
|
||||||
beq ++
|
|
||||||
+ ldx #1
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
|
||||||
else -> throw AssemblyError("invalid reg dt for word not")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack not")
|
|
||||||
else -> throw AssemblyError("no asm gen for in-place not of uword for ${target.kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("boolean-not of invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
|
@ -28,7 +28,7 @@ dependencies {
|
|||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
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 "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +135,8 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
xml.attr("nosysinit", options.noSysInit.toString())
|
xml.attr("nosysinit", options.noSysInit.toString())
|
||||||
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
|
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
|
||||||
xml.attr("optimize", options.optimize.toString())
|
xml.attr("optimize", options.optimize.toString())
|
||||||
|
if(options.evalStackBaseAddress!=null)
|
||||||
|
xml.attr("evalstackbase", options.evalStackBaseAddress!!.toString())
|
||||||
if(options.zpReserved.isNotEmpty()) {
|
if(options.zpReserved.isNotEmpty()) {
|
||||||
xml.startChildren()
|
xml.startChildren()
|
||||||
options.zpReserved.forEach {
|
options.zpReserved.forEach {
|
||||||
@ -144,6 +146,15 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
xml.endElt()
|
xml.endElt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(options.symbolDefs.isNotEmpty()) {
|
||||||
|
xml.startChildren()
|
||||||
|
options.symbolDefs.forEach { name, value ->
|
||||||
|
xml.elt("symboldef")
|
||||||
|
xml.attr("name", name)
|
||||||
|
xml.attr("value", value)
|
||||||
|
xml.endElt()
|
||||||
|
}
|
||||||
|
}
|
||||||
xml.endElt()
|
xml.endElt()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +183,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
is PtMemoryByte -> write(it)
|
is PtMemoryByte -> write(it)
|
||||||
is PtMemMapped -> write(it)
|
is PtMemMapped -> write(it)
|
||||||
is PtNumber -> write(it)
|
is PtNumber -> write(it)
|
||||||
is PtPipe -> write(it)
|
|
||||||
is PtPostIncrDecr -> write(it)
|
is PtPostIncrDecr -> write(it)
|
||||||
is PtPrefix -> write(it)
|
is PtPrefix -> write(it)
|
||||||
is PtRange -> write(it)
|
is PtRange -> write(it)
|
||||||
@ -204,14 +214,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
xml.endElt()
|
xml.endElt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun write(pipe: PtPipe) {
|
|
||||||
xml.elt("pipe")
|
|
||||||
xml.attr("type", pipe.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
pipe.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(array: PtArray) {
|
private fun write(array: PtArray) {
|
||||||
xml.elt("array")
|
xml.elt("array")
|
||||||
xml.attr("type", array.type.name)
|
xml.attr("type", array.type.name)
|
||||||
|
@ -3,6 +3,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm"
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
id "io.kotest" version "0.3.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -24,13 +25,14 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':virtualmachine')
|
|
||||||
implementation project(':codeAst')
|
implementation project(':codeAst')
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
|
implementation project(':virtualmachine')
|
||||||
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 "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -42,6 +44,22 @@ sourceSets {
|
|||||||
srcDirs = ["${project.projectDir}/res"]
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDir "${project.projectDir}/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,14 @@
|
|||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
<orderEntry type="module" module-name="codeAst" />
|
<orderEntry type="module" module-name="codeAst" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="virtualmachine" />
|
<orderEntry type="module" module-name="virtualmachine" />
|
||||||
|
@ -13,8 +13,7 @@ import kotlin.io.path.bufferedWriter
|
|||||||
import kotlin.io.path.div
|
import kotlin.io.path.div
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyProgram(override val name: String,
|
class AssemblyProgram(override val name: String, private val allocations: VariableAllocator
|
||||||
private val allocations: VariableAllocator
|
|
||||||
) : IAssemblyProgram {
|
) : IAssemblyProgram {
|
||||||
|
|
||||||
private val globalInits = mutableListOf<VmCodeLine>()
|
private val globalInits = mutableListOf<VmCodeLine>()
|
||||||
@ -68,11 +67,12 @@ internal class AssemblyProgram(override val name: String,
|
|||||||
|
|
||||||
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
|
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
|
||||||
fun addBlock(block: VmCodeChunk) = blocks.add(block)
|
fun addBlock(block: VmCodeChunk) = blocks.add(block)
|
||||||
|
fun getBlocks(): List<VmCodeChunk> = blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class VmCodeLine
|
sealed class VmCodeLine
|
||||||
|
|
||||||
internal class VmCodeInstruction(
|
class VmCodeInstruction(
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
type: VmDataType?=null,
|
type: VmDataType?=null,
|
||||||
reg1: Int?=null, // 0-$ffff
|
reg1: Int?=null, // 0-$ffff
|
||||||
@ -82,7 +82,7 @@ internal class VmCodeInstruction(
|
|||||||
value: Int?=null, // 0-$ffff
|
value: Int?=null, // 0-$ffff
|
||||||
fpValue: Float?=null,
|
fpValue: Float?=null,
|
||||||
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
|
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
|
||||||
): VmCodeLine() {
|
): VmCodeLine() {
|
||||||
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
|
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -109,12 +109,12 @@ internal class VmCodeInstruction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
|
class VmCodeLabel(val name: List<String>): VmCodeLine()
|
||||||
internal class VmCodeComment(val comment: String): VmCodeLine()
|
internal class VmCodeComment(val comment: String): VmCodeLine()
|
||||||
|
|
||||||
internal class VmCodeChunk(initial: VmCodeLine? = null) {
|
class VmCodeChunk(initial: VmCodeLine? = null) {
|
||||||
val lines = mutableListOf<VmCodeLine>()
|
val lines = mutableListOf<VmCodeLine>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package prog8.codegen.virtual
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.AssemblyError
|
import prog8.code.core.*
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.SignedDatatypes
|
|
||||||
import prog8.vm.Opcode
|
import prog8.vm.Opcode
|
||||||
import prog8.vm.VmDataType
|
import prog8.vm.VmDataType
|
||||||
|
|
||||||
@ -33,7 +31,9 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
|
|||||||
else
|
else
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(assignment)
|
||||||
} else if(array!=null) {
|
} else if(array!=null) {
|
||||||
// TODO in-place array element assignment?
|
// 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
|
||||||
|
// will be optimized later and have the double assignments removed.
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(assignment)
|
||||||
} else {
|
} else {
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(assignment)
|
||||||
@ -110,9 +110,6 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
|
|||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||||
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
||||||
}
|
}
|
||||||
"not" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird prefix operator")
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -159,6 +156,24 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
|
|||||||
val variable = array.variable.targetName
|
val variable = array.variable.targetName
|
||||||
var variableAddr = codeGen.allocations.get(variable)
|
var variableAddr = codeGen.allocations.get(variable)
|
||||||
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
||||||
|
|
||||||
|
if(array.variable.type==DataType.UWORD) {
|
||||||
|
// indexing a pointer var instead of a real array or string
|
||||||
|
if(itemsize!=1)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
|
if(array.index.type!=DataType.UBYTE)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
|
val idxReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(array.index, idxReg, -1)
|
||||||
|
if(zero) {
|
||||||
|
// there's no STOREZIX instruction
|
||||||
|
resultRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, value = variableAddr)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
val fixedIndex = constIntValue(array.index)
|
val fixedIndex = constIntValue(array.index)
|
||||||
if(zero) {
|
if(zero) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
|
@ -41,12 +41,11 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
"mkword" -> funcMkword(call, resultRegister)
|
"mkword" -> funcMkword(call, resultRegister)
|
||||||
"sort" -> funcSort(call)
|
"sort" -> funcSort(call)
|
||||||
"reverse" -> funcReverse(call)
|
"reverse" -> funcReverse(call)
|
||||||
"swap" -> funcSwap(call)
|
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
|
||||||
"rol" -> funcRolRor2(Opcode.ROXL, call, resultRegister)
|
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
|
||||||
"ror" -> funcRolRor2(Opcode.ROXR, call, resultRegister)
|
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
|
||||||
"rol2" -> funcRolRor2(Opcode.ROL, call, resultRegister)
|
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
|
||||||
"ror2" -> funcRolRor2(Opcode.ROR, call, resultRegister)
|
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||||
else -> TODO("builtinfunc ${call.name}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,21 +111,21 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val andReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80)
|
val compareReg = codeGen.vmRegisters.nextFree()
|
||||||
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister)
|
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
||||||
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, labelSymbol = notNegativeLabel)
|
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
|
||||||
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||||
code += VmCodeLabel(notNegativeLabel)
|
code += VmCodeLabel(notNegativeLabel)
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val andReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000)
|
val compareReg = codeGen.vmRegisters.nextFree()
|
||||||
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister)
|
code += VmCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
||||||
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, labelSymbol = notNegativeLabel)
|
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
|
||||||
code += VmCodeLabel(notNegativeLabel)
|
code += VmCodeLabel(notNegativeLabel)
|
||||||
}
|
}
|
||||||
@ -184,19 +183,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSwap(call: PtBuiltinFunctionCall): VmCodeChunk {
|
|
||||||
val left = call.args[0]
|
|
||||||
val right = call.args[1]
|
|
||||||
val leftReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val rightReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += exprGen.translateExpression(left, leftReg, -1)
|
|
||||||
code += exprGen.translateExpression(right, rightReg, -1)
|
|
||||||
code += assignRegisterTo(left, rightReg)
|
|
||||||
code += assignRegisterTo(right, leftReg)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk {
|
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||||
@ -224,7 +210,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
DataType.ARRAY_UW -> Syscall.SORT_UWORD
|
DataType.ARRAY_UW -> Syscall.SORT_UWORD
|
||||||
DataType.ARRAY_W -> Syscall.SORT_WORD
|
DataType.ARRAY_W -> Syscall.SORT_WORD
|
||||||
DataType.STR -> Syscall.SORT_UBYTE
|
DataType.STR -> Syscall.SORT_UBYTE
|
||||||
DataType.ARRAY_F -> throw java.lang.IllegalArgumentException("sorting a floating point array is not supported")
|
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
|
||||||
else -> throw IllegalArgumentException("weird type to sort")
|
else -> throw IllegalArgumentException("weird type to sort")
|
||||||
}
|
}
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
@ -368,7 +354,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
val vmDt = codeGen.vmType(call.args[0].type)
|
val vmDt = codeGen.vmType(call.args[0].type)
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||||
|
@ -40,7 +40,7 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
internal val errors: IErrorReporter
|
internal val errors: IErrorReporter
|
||||||
): IAssemblyGenerator {
|
): IAssemblyGenerator {
|
||||||
|
|
||||||
internal val allocations = VariableAllocator(symbolTable, program, errors)
|
internal val allocations = VariableAllocator(symbolTable, program)
|
||||||
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)
|
||||||
@ -58,11 +58,21 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(options.symbolDefs.isNotEmpty())
|
||||||
|
throw AssemblyError("virtual target doesn't support symbols defined on the commandline")
|
||||||
|
if(options.evalStackBaseAddress!=null)
|
||||||
|
throw AssemblyError("virtual target doesn't use eval-stack")
|
||||||
|
|
||||||
for (block in program.allBlocks()) {
|
for (block in program.allBlocks()) {
|
||||||
vmprog.addBlock(translate(block))
|
vmprog.addBlock(translate(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
|
if(options.optimize) {
|
||||||
|
val optimizer = VmPeepholeOptimizer(vmprog)
|
||||||
|
optimizer.optimize()
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Vm codegen: virtual registers=${vmRegisters.peekNext()} memory usage=${allocations.freeMem}")
|
||||||
|
|
||||||
return vmprog
|
return vmprog
|
||||||
}
|
}
|
||||||
@ -72,6 +82,7 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
val code = when(node) {
|
val code = when(node) {
|
||||||
is PtBlock -> translate(node)
|
is PtBlock -> translate(node)
|
||||||
is PtSub -> translate(node)
|
is PtSub -> translate(node)
|
||||||
|
is PtAsmSub -> translate(node)
|
||||||
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
|
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
|
||||||
is PtVariable -> VmCodeChunk() // var should be looked up via symbol table
|
is PtVariable -> VmCodeChunk() // var should be looked up via symbol table
|
||||||
is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table
|
is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table
|
||||||
@ -84,7 +95,6 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
is PtReturn -> translate(node)
|
is PtReturn -> translate(node)
|
||||||
is PtJump -> translate(node)
|
is PtJump -> translate(node)
|
||||||
is PtWhen -> translate(node)
|
is PtWhen -> translate(node)
|
||||||
is PtPipe -> expressionEval.translate(node, 0)
|
|
||||||
is PtForLoop -> translate(node)
|
is PtForLoop -> translate(node)
|
||||||
is PtIfElse -> translate(node)
|
is PtIfElse -> translate(node)
|
||||||
is PtPostIncrDecr -> translate(node)
|
is PtPostIncrDecr -> translate(node)
|
||||||
@ -94,7 +104,6 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
is PtConditionalBranch -> translate(node)
|
is PtConditionalBranch -> translate(node)
|
||||||
is PtInlineAssembly -> VmCodeChunk(VmCodeInlineAsm(node.assembly))
|
is PtInlineAssembly -> VmCodeChunk(VmCodeInlineAsm(node.assembly))
|
||||||
is PtIncludeBinary -> VmCodeChunk(VmCodeInlineBinary(node.file, node.offset, node.length))
|
is PtIncludeBinary -> VmCodeChunk(VmCodeInlineBinary(node.file, node.offset, node.length))
|
||||||
is PtAsmSub -> TODO("asmsub not yet supported on virtual machine target ${node.position}")
|
|
||||||
is PtAddressOf,
|
is PtAddressOf,
|
||||||
is PtContainmentCheck,
|
is PtContainmentCheck,
|
||||||
is PtMemoryByte,
|
is PtMemoryByte,
|
||||||
@ -266,14 +275,8 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
||||||
code += VmCodeLabel(loopLabel)
|
code += VmCodeLabel(loopLabel)
|
||||||
code += translateNode(forLoop.statements)
|
code += translateNode(forLoop.statements)
|
||||||
if(step<3) {
|
|
||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
} else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
code += addConstReg(loopvarDt, indexReg, step)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
}
|
|
||||||
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
|
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
|
||||||
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
|
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
|
||||||
return code
|
return code
|
||||||
@ -305,14 +308,8 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
||||||
code += VmCodeLabel(loopLabel)
|
code += VmCodeLabel(loopLabel)
|
||||||
code += translateNode(forLoop.statements)
|
code += translateNode(forLoop.statements)
|
||||||
if(step<3) {
|
|
||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
} else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
code += addConstReg(loopvarDt, indexReg, step)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
}
|
|
||||||
code += if(rangeEndWrapped==0) {
|
code += if(rangeEndWrapped==0) {
|
||||||
VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
|
VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
|
||||||
} else {
|
} else {
|
||||||
@ -340,14 +337,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val valueReg = vmRegisters.nextFree()
|
code += if(value>0) {
|
||||||
if(value>0) {
|
VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
|
} else {
|
||||||
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = valueReg)
|
VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
|
||||||
}
|
|
||||||
else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
|
|
||||||
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = valueReg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,18 +367,13 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val valueReg = vmRegisters.nextFree()
|
val valueReg = vmRegisters.nextFree()
|
||||||
val operandReg = vmRegisters.nextFree()
|
|
||||||
if(value>0) {
|
if(value>0) {
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
|
code += VmCodeInstruction(Opcode.ADDM, dt, reg1=valueReg, value=address.toInt())
|
||||||
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = operandReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
|
code += VmCodeInstruction(Opcode.SUBM, dt, reg1=valueReg, value=address.toInt())
|
||||||
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = operandReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,12 +384,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
if(factor==1f)
|
if(factor==1f)
|
||||||
return code
|
return code
|
||||||
if(factor==0f) {
|
code += if(factor==0f) {
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
||||||
} else {
|
} else {
|
||||||
val factorReg = vmRegisters.nextFreeFloat()
|
VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
|
||||||
code += VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
|
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@ -437,13 +423,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
code += VmCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
code += VmCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||||
} else {
|
} else {
|
||||||
if (factor == 0) {
|
code += if (factor == 0) {
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
||||||
}
|
} else {
|
||||||
else {
|
VmCodeInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
|
||||||
val factorReg = vmRegisters.nextFree()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
|
||||||
code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=factorReg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -480,12 +463,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
if(factor==1f)
|
if(factor==1f)
|
||||||
return code
|
return code
|
||||||
if(factor==0f) {
|
code += if(factor==0f) {
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
||||||
} else {
|
} else {
|
||||||
val factorReg = vmRegisters.nextFreeFloat()
|
VmCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
|
||||||
code += VmCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
|
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@ -511,14 +492,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
val pow2 = powersOfTwo.indexOf(factor)
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
if(pow2==1) {
|
if(pow2==1 && !signed) {
|
||||||
// just shift 1 bit
|
code += VmCodeInstruction(Opcode.LSR, dt, reg1=reg) // simple single bit shift
|
||||||
code += if(signed)
|
|
||||||
VmCodeInstruction(Opcode.ASR, dt, reg1=reg)
|
|
||||||
else
|
|
||||||
VmCodeInstruction(Opcode.LSR, dt, reg1=reg)
|
|
||||||
}
|
}
|
||||||
else if(pow2>=1) {
|
else if(pow2>=1 &&!signed) {
|
||||||
// just shift multiple bits
|
// just shift multiple bits
|
||||||
val pow2reg = vmRegisters.nextFree()
|
val pow2reg = vmRegisters.nextFree()
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
@ -527,16 +504,13 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
else
|
else
|
||||||
VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
||||||
} else {
|
} else {
|
||||||
if (factor == 0) {
|
code += if (factor == 0) {
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||||
}
|
} else {
|
||||||
else {
|
if(signed)
|
||||||
val factorReg = vmRegisters.nextFree()
|
VmCodeInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
|
||||||
code += if(signed)
|
|
||||||
VmCodeInstruction(Opcode.DIVS, dt, reg1=reg, reg2=factorReg)
|
|
||||||
else
|
else
|
||||||
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, reg2=factorReg)
|
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -547,14 +521,10 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
val pow2 = powersOfTwo.indexOf(factor)
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
if(pow2==1) {
|
if(pow2==1 && !signed) {
|
||||||
// just shift 1 bit
|
code += VmCodeInstruction(Opcode.LSRM, dt, value=address) // just simple bit shift
|
||||||
code += if(signed)
|
|
||||||
VmCodeInstruction(Opcode.ASRM, dt, value=address)
|
|
||||||
else
|
|
||||||
VmCodeInstruction(Opcode.LSRM, dt, value=address)
|
|
||||||
}
|
}
|
||||||
else if(pow2>=1) {
|
else if(pow2>=1 && !signed) {
|
||||||
// just shift multiple bits
|
// just shift multiple bits
|
||||||
val pow2reg = vmRegisters.nextFree()
|
val pow2reg = vmRegisters.nextFree()
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
@ -792,6 +762,17 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translate(sub: PtAsmSub): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += VmCodeComment("ASMSUB: ${sub.scopedName}")
|
||||||
|
code += VmCodeLabel(sub.scopedName)
|
||||||
|
for (child in sub.children) {
|
||||||
|
code += translateNode(child)
|
||||||
|
}
|
||||||
|
code += VmCodeComment("ASMSUB-END '${sub.name}'")
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(block: PtBlock): VmCodeChunk {
|
private fun translate(block: PtBlock): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
||||||
@ -806,6 +787,7 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
|
|
||||||
internal fun vmType(type: DataType): VmDataType {
|
internal fun vmType(type: DataType): VmDataType {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
|
DataType.BOOL,
|
||||||
DataType.UBYTE,
|
DataType.UBYTE,
|
||||||
DataType.BYTE -> VmDataType.BYTE
|
DataType.BYTE -> VmDataType.BYTE
|
||||||
DataType.UWORD,
|
DataType.UWORD,
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package prog8.codegen.virtual
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.StRomSub
|
||||||
import prog8.code.StStaticVariable
|
import prog8.code.StStaticVariable
|
||||||
import prog8.code.StSub
|
import prog8.code.StSub
|
||||||
|
import prog8.code.StSubroutineParameter
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.vm.Opcode
|
import prog8.vm.Opcode
|
||||||
@ -63,7 +65,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
||||||
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
is PtPipe -> code += translate(expr, resultRegister)
|
|
||||||
is PtRange,
|
is PtRange,
|
||||||
is PtArray,
|
is PtArray,
|
||||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||||
@ -72,46 +73,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
|
|
||||||
val segments = pipe.segments
|
|
||||||
var valueDt = segments[0].type
|
|
||||||
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
|
|
||||||
|
|
||||||
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
|
|
||||||
return when (segment) {
|
|
||||||
is PtFunctionCall -> {
|
|
||||||
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
|
|
||||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
|
||||||
segWithArg.children.addAll(segment.args)
|
|
||||||
segWithArg
|
|
||||||
}
|
|
||||||
is PtBuiltinFunctionCall -> {
|
|
||||||
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.hasNoSideEffects, segment.type, segment.position)
|
|
||||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
|
||||||
segWithArg.children.addAll(segment.args)
|
|
||||||
segWithArg
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird segment type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += translateExpression(segments[0], valueReg, -1)
|
|
||||||
for (segment in segments.subList(1, segments.size-1)) {
|
|
||||||
val sourceReg = valueReg
|
|
||||||
val sourceDt = valueDt
|
|
||||||
if(segment.type!=valueDt) {
|
|
||||||
valueDt = segment.type
|
|
||||||
valueReg = codeGen.vmRegisters.nextFree()
|
|
||||||
}
|
|
||||||
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
|
|
||||||
code += translateExpression(segmentWithImplicitArgument, valueReg, -1)
|
|
||||||
}
|
|
||||||
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
|
|
||||||
code += translateExpression(segWithArg, resultRegister, -1)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
|
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
|
||||||
@ -149,6 +110,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
val idxReg = codeGen.vmRegisters.nextFree()
|
val idxReg = codeGen.vmRegisters.nextFree()
|
||||||
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
||||||
|
|
||||||
|
if(arrayIx.variable.type==DataType.UWORD) {
|
||||||
|
// indexing a pointer var instead of a real array or string
|
||||||
|
if(eltSize!=1)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
|
if(arrayIx.index.type!=DataType.UBYTE)
|
||||||
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
|
code += translateExpression(arrayIx.index, idxReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
if(arrayIx.index is PtNumber) {
|
if(arrayIx.index is PtNumber) {
|
||||||
// optimized code when index is known - just calculate the memory address here
|
// optimized code when index is known - just calculate the memory address here
|
||||||
val memOffset = (arrayIx.index as PtNumber).number.toInt() * eltSize
|
val memOffset = (arrayIx.index as PtNumber).number.toInt() * eltSize
|
||||||
@ -178,13 +151,8 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
|
||||||
}
|
}
|
||||||
"~" -> {
|
"~" -> {
|
||||||
val regMask = codeGen.vmRegisters.nextFree()
|
|
||||||
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)
|
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
|
||||||
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=regMask)
|
|
||||||
}
|
|
||||||
"not" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird prefix operator")
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
}
|
}
|
||||||
@ -253,18 +221,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when(cast.value.type) {
|
code += when(cast.value.type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
code += VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
code += VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
code += VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
code += VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird cast value type")
|
else -> throw AssemblyError("weird cast value type")
|
||||||
}
|
}
|
||||||
@ -283,9 +251,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
"*" -> operatorMultiply(binExpr, vmDt, resultRegister, resultFpRegister)
|
"*" -> operatorMultiply(binExpr, vmDt, resultRegister, resultFpRegister)
|
||||||
"/" -> operatorDivide(binExpr, vmDt, resultRegister, resultFpRegister, signed)
|
"/" -> operatorDivide(binExpr, vmDt, resultRegister, resultFpRegister, signed)
|
||||||
"%" -> operatorModulo(binExpr, vmDt, resultRegister)
|
"%" -> operatorModulo(binExpr, vmDt, resultRegister)
|
||||||
"|", "or" -> operatorOr(binExpr, vmDt, resultRegister)
|
"|" -> operatorOr(binExpr, vmDt, resultRegister)
|
||||||
"&", "and" -> operatorAnd(binExpr, vmDt, resultRegister)
|
"&" -> operatorAnd(binExpr, vmDt, resultRegister)
|
||||||
"^", "xor" -> operatorXor(binExpr, vmDt, resultRegister)
|
"^" -> operatorXor(binExpr, vmDt, resultRegister)
|
||||||
"<<" -> operatorShiftLeft(binExpr, vmDt, resultRegister)
|
"<<" -> operatorShiftLeft(binExpr, vmDt, resultRegister)
|
||||||
">>" -> operatorShiftRight(binExpr, vmDt, resultRegister, signed)
|
">>" -> operatorShiftRight(binExpr, vmDt, resultRegister, signed)
|
||||||
"==" -> operatorEquals(binExpr, vmDt, resultRegister, false)
|
"==" -> operatorEquals(binExpr, vmDt, resultRegister, false)
|
||||||
@ -420,13 +388,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
comparisonCall.children.add(binExpr.left)
|
comparisonCall.children.add(binExpr.left)
|
||||||
comparisonCall.children.add(binExpr.right)
|
comparisonCall.children.add(binExpr.right)
|
||||||
code += translate(comparisonCall, resultRegister, -1)
|
code += translate(comparisonCall, resultRegister, -1)
|
||||||
if(notEquals) {
|
if(!notEquals)
|
||||||
val maskReg = codeGen.vmRegisters.nextFree()
|
code += VmCodeInstruction(Opcode.INV, vmDt, reg1=resultRegister)
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=maskReg, value=1)
|
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
|
||||||
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=maskReg)
|
|
||||||
} else {
|
|
||||||
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
@ -496,10 +460,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
|
|
||||||
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,10 +482,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
|
|
||||||
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,10 +504,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
|
|
||||||
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,9 +529,14 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
throw IllegalArgumentException("floating-point modulo not supported")
|
throw IllegalArgumentException("floating-point modulo not supported")
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,9 +557,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
code += if(signed)
|
code += if(signed)
|
||||||
VmCodeInstruction(Opcode.DIVS, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
VmCodeInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
else
|
else
|
||||||
VmCodeInstruction(Opcode.DIV, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
VmCodeInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
@ -584,12 +568,20 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += codeGen.divideByConst(vmDt, resultRegister, factor, signed)
|
code += codeGen.divideByConst(vmDt, resultRegister, factor, signed)
|
||||||
} else {
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += if (signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += if(signed)
|
code += if (signed)
|
||||||
VmCodeInstruction(Opcode.DIVS, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
VmCodeInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
else
|
else
|
||||||
VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
VmCodeInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -643,7 +635,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
code += VmCodeInstruction(Opcode.MUL, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
code += VmCodeInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
|
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
|
||||||
@ -658,7 +650,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -697,10 +689,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += VmCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
|
code += VmCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||||
|
} else {
|
||||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
code += VmCodeInstruction(Opcode.SUB, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
|
code += VmCodeInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if((binExpr.right as? PtNumber)?.number==1.0) {
|
if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||||
@ -708,10 +705,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -753,10 +755,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||||
|
} else {
|
||||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
code += VmCodeInstruction(Opcode.ADD, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
|
code += VmCodeInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if((binExpr.left as? PtNumber)?.number==1.0) {
|
if((binExpr.left as? PtNumber)?.number==1.0) {
|
||||||
@ -768,10 +775,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(binExpr.right is PtNumber) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
code += VmCodeInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -802,9 +814,10 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun translate(fcall: PtFunctionCall, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
fun translate(fcall: PtFunctionCall, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
|
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.functionName)) {
|
||||||
|
is StSub -> {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
|
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||||
val paramDt = codeGen.vmType(parameter.type)
|
val paramDt = codeGen.vmType(parameter.type)
|
||||||
if(codeGen.isZero(arg)) {
|
if(codeGen.isZero(arg)) {
|
||||||
if (paramDt == VmDataType.FLOAT) {
|
if (paramDt == VmDataType.FLOAT) {
|
||||||
@ -842,5 +855,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
is StRomSub -> {
|
||||||
|
throw AssemblyError("virtual machine doesn't yet support calling romsub $fcall")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid node type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import prog8.code.SymbolTable
|
|||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram, errors: IErrorReporter) {
|
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram) {
|
||||||
|
|
||||||
private val allocations = mutableMapOf<List<String>, Int>()
|
private val allocations = mutableMapOf<List<String>, Int>()
|
||||||
private var freeMemoryStart: Int
|
private var freeMemoryStart: Int
|
||||||
@ -26,6 +26,18 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
|
|||||||
allocations[variable.scopedName] = nextLocation
|
allocations[variable.scopedName] = nextLocation
|
||||||
nextLocation += memsize
|
nextLocation += memsize
|
||||||
}
|
}
|
||||||
|
for (memvar in st.allMemMappedVariables) {
|
||||||
|
// TODO virtual machine doesn't have memory mapped variables, so treat them as regular allocated variables for now
|
||||||
|
val memsize =
|
||||||
|
when (memvar.dt) {
|
||||||
|
in NumericDatatypes -> program.memsizer.memorySize(memvar.dt)
|
||||||
|
in ArrayDatatypes -> program.memsizer.memorySize(memvar.dt, memvar.length!!)
|
||||||
|
else -> throw InternalCompilerException("weird dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
allocations[memvar.scopedName] = nextLocation
|
||||||
|
nextLocation += memsize
|
||||||
|
}
|
||||||
|
|
||||||
freeMemoryStart = nextLocation
|
freeMemoryStart = nextLocation
|
||||||
}
|
}
|
||||||
@ -48,8 +60,8 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
|
|||||||
DataType.FLOAT -> (variable.initialNumericValue ?: 0.0).toString()
|
DataType.FLOAT -> (variable.initialNumericValue ?: 0.0).toString()
|
||||||
in NumericDatatypes -> (variable.initialNumericValue ?: 0).toHex()
|
in NumericDatatypes -> (variable.initialNumericValue ?: 0).toHex()
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
val encoded = program.encoding.encodeString(variable.initialStringValue!!.first, variable.initialStringValue!!.second)
|
val encoded = program.encoding.encodeString(variable.initialStringValue!!.first, variable.initialStringValue!!.second) + listOf(0u)
|
||||||
encoded.joinToString(",") { it.toInt().toHex() } + ",0"
|
encoded.joinToString(",") { it.toInt().toHex() }
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
if(variable.initialArrayValue!=null) {
|
if(variable.initialArrayValue!=null) {
|
||||||
@ -69,6 +81,25 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
|
|||||||
}
|
}
|
||||||
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
|
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
|
||||||
}
|
}
|
||||||
|
for (variable in st.allMemMappedVariables) {
|
||||||
|
val location = allocations.getValue(variable.scopedName)
|
||||||
|
val typeStr = when(variable.dt) {
|
||||||
|
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
|
||||||
|
DataType.BYTE, DataType.ARRAY_B -> "byte"
|
||||||
|
DataType.UWORD, DataType.ARRAY_UW -> "uword"
|
||||||
|
DataType.WORD, DataType.ARRAY_W -> "word"
|
||||||
|
DataType.FLOAT, DataType.ARRAY_F -> "float"
|
||||||
|
else -> throw InternalCompilerException("weird dt")
|
||||||
|
}
|
||||||
|
val value = when(variable.dt) {
|
||||||
|
DataType.FLOAT -> "0.0"
|
||||||
|
in NumericDatatypes -> "0"
|
||||||
|
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
|
||||||
|
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
|
||||||
|
else -> throw InternalCompilerException("weird dt for mem mapped var")
|
||||||
|
}
|
||||||
|
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
|
||||||
|
}
|
||||||
return mm
|
return mm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
188
codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt
Normal file
188
codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.vm.Instruction
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.VmDataType
|
||||||
|
|
||||||
|
|
||||||
|
class VmPeepholeOptimizer(private val vmprog: AssemblyProgram) {
|
||||||
|
fun optimize() {
|
||||||
|
vmprog.getBlocks().forEach { block ->
|
||||||
|
do {
|
||||||
|
val indexedInstructions = block.lines.withIndex()
|
||||||
|
.filter { it.value is VmCodeInstruction }
|
||||||
|
.map { IndexedValue(it.index, (it.value as VmCodeInstruction).ins) }
|
||||||
|
val changed = removeNops(block, indexedInstructions)
|
||||||
|
|| removeDoubleLoadsAndStores(block, indexedInstructions) // TODO not yet implemented
|
||||||
|
|| removeUselessArithmetic(block, indexedInstructions)
|
||||||
|
|| removeWeirdBranches(block, indexedInstructions)
|
||||||
|
|| removeDoubleSecClc(block, indexedInstructions)
|
||||||
|
|| cleanupPushPop(block, indexedInstructions)
|
||||||
|
// TODO other optimizations:
|
||||||
|
// more complex optimizations such as unused registers
|
||||||
|
} while(changed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupPushPop(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
// push followed by pop to same target, or different target->replace with load
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
if(ins.opcode==Opcode.PUSH) {
|
||||||
|
if(idx < block.lines.size-1) {
|
||||||
|
val insAfter = block.lines[idx+1] as? VmCodeInstruction
|
||||||
|
if(insAfter!=null && insAfter.ins.opcode ==Opcode.POP) {
|
||||||
|
if(ins.reg1==insAfter.ins.reg1) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
} else {
|
||||||
|
block.lines[idx] = VmCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
|
||||||
|
block.lines.removeAt(idx+1)
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun removeDoubleSecClc(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
// double sec, clc
|
||||||
|
// sec+clc or clc+sec
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
if(ins.opcode==Opcode.SEC || ins.opcode==Opcode.CLC) {
|
||||||
|
if(idx < block.lines.size-1) {
|
||||||
|
val insAfter = block.lines[idx+1] as? VmCodeInstruction
|
||||||
|
if(insAfter?.ins?.opcode == ins.opcode) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
else if(ins.opcode==Opcode.SEC && insAfter?.ins?.opcode==Opcode.CLC) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
else if(ins.opcode==Opcode.CLC && insAfter?.ins?.opcode==Opcode.SEC) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeWeirdBranches(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
// jump/branch to label immediately below
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
if(ins.opcode==Opcode.JUMP && ins.labelSymbol!=null) {
|
||||||
|
// if jumping to label immediately following this
|
||||||
|
if(idx < block.lines.size-1) {
|
||||||
|
val label = block.lines[idx+1] as? VmCodeLabel
|
||||||
|
if(label?.name == ins.labelSymbol) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUselessArithmetic(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
when (ins.opcode) {
|
||||||
|
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||||
|
if (ins.value == 1) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.ADD, Opcode.SUB -> {
|
||||||
|
if (ins.value == 1) {
|
||||||
|
block.lines[idx] = VmCodeInstruction(
|
||||||
|
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
||||||
|
ins.type,
|
||||||
|
ins.reg1
|
||||||
|
)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 0) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.AND -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
block.lines[idx] = VmCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 65535 && ins.type == VmDataType.WORD) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.OR -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
|
||||||
|
block.lines[idx] = VmCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.XOR -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeNops(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
if (ins.opcode == Opcode.NOP) {
|
||||||
|
changed = true
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeDoubleLoadsAndStores(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.forEach { (idx, ins) ->
|
||||||
|
|
||||||
|
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
|
||||||
|
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
|
||||||
|
// TODO: detect multiple float ffrom/fto to the same target, only keep first
|
||||||
|
// TODO: detect multiple sequential rnd with same reg1, only keep one
|
||||||
|
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
|
||||||
|
// TODO: detect multiple same ands, ors; only keep first
|
||||||
|
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ICodeChange { // TODO not used? remove?
|
||||||
|
fun perform(block: VmCodeChunk)
|
||||||
|
|
||||||
|
class Remove(val idx: Int): ICodeChange {
|
||||||
|
override fun perform(block: VmCodeChunk) {
|
||||||
|
block.lines.removeAt(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
codeGenVirtual/test/Dummies.kt
Normal file
22
codeGenVirtual/test/Dummies.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package prog8tests.vm.helpers
|
||||||
|
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
|
||||||
|
|
||||||
|
internal object DummyMemsizer : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType) = 0
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object DummyStringEncoder : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
170
codeGenVirtual/test/TestVmPeepholeOpt.kt
Normal file
170
codeGenVirtual/test/TestVmPeepholeOpt.kt
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package prog8tests.vm
|
||||||
|
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
import prog8.codegen.virtual.*
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.VmDataType
|
||||||
|
import prog8tests.vm.helpers.DummyMemsizer
|
||||||
|
import prog8tests.vm.helpers.DummyStringEncoder
|
||||||
|
|
||||||
|
class TestVmPeepholeOpt: FunSpec({
|
||||||
|
fun makeVmProgram(lines: List<VmCodeLine>): Pair<AssemblyProgram, VariableAllocator> {
|
||||||
|
val st = SymbolTable()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val allocations = VariableAllocator(st, program)
|
||||||
|
val asm = AssemblyProgram("test", allocations)
|
||||||
|
val block = VmCodeChunk()
|
||||||
|
for(line in lines)
|
||||||
|
block += line
|
||||||
|
asm.addBlock(block)
|
||||||
|
return Pair(asm, allocations)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AssemblyProgram.lines(): List<VmCodeLine> = this.getBlocks().flatMap { it.lines }
|
||||||
|
|
||||||
|
test("remove nops") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("dummy")),
|
||||||
|
VmCodeInstruction(Opcode.NOP),
|
||||||
|
VmCodeInstruction(Opcode.NOP)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 3
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
asm.lines().size shouldBe 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove jmp to label below") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("label")), // removed
|
||||||
|
VmCodeLabel(listOf("label")),
|
||||||
|
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("label2")), // removed
|
||||||
|
VmCodeInstruction(Opcode.NOP), // removed
|
||||||
|
VmCodeLabel(listOf("label2")),
|
||||||
|
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("label3")),
|
||||||
|
VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
|
||||||
|
VmCodeLabel(listOf("label3"))
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 8
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 5
|
||||||
|
(lines[0] as VmCodeLabel).name shouldBe listOf("label")
|
||||||
|
(lines[1] as VmCodeLabel).name shouldBe listOf("label2")
|
||||||
|
(lines[2] as VmCodeInstruction).ins.opcode shouldBe Opcode.JUMP
|
||||||
|
(lines[3] as VmCodeInstruction).ins.opcode shouldBe Opcode.INC
|
||||||
|
(lines[4] as VmCodeLabel).name shouldBe listOf("label3")
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove double sec/clc") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.SEC),
|
||||||
|
VmCodeInstruction(Opcode.SEC),
|
||||||
|
VmCodeInstruction(Opcode.SEC),
|
||||||
|
VmCodeInstruction(Opcode.CLC),
|
||||||
|
VmCodeInstruction(Opcode.CLC),
|
||||||
|
VmCodeInstruction(Opcode.CLC)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 6
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 1
|
||||||
|
(lines[0] as VmCodeInstruction).ins.opcode shouldBe Opcode.CLC
|
||||||
|
}
|
||||||
|
|
||||||
|
test("push followed by pop") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
|
||||||
|
VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
|
||||||
|
VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
|
||||||
|
VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 4
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 1
|
||||||
|
(lines[0] as VmCodeInstruction).ins.opcode shouldBe Opcode.LOADR
|
||||||
|
(lines[0] as VmCodeInstruction).ins.reg1 shouldBe 222
|
||||||
|
(lines[0] as VmCodeInstruction).ins.reg2 shouldBe 99
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove useless div/mul, add/sub") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
|
||||||
|
VmCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
|
||||||
|
VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
|
||||||
|
VmCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
|
||||||
|
VmCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
|
||||||
|
VmCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 10
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("replace add/sub 1 by inc/dec") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 2
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 2
|
||||||
|
(lines[0] as VmCodeInstruction).ins.opcode shouldBe Opcode.INC
|
||||||
|
(lines[1] as VmCodeInstruction).ins.opcode shouldBe Opcode.DEC
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove useless and/or/xor") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
|
||||||
|
VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
|
||||||
|
VmCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
|
||||||
|
VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
|
||||||
|
VmCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 8
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("replace and/or/xor by constant number") {
|
||||||
|
val(asm, _) = makeVmProgram(listOf(
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
|
||||||
|
VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
|
||||||
|
VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
|
||||||
|
VmCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
|
||||||
|
))
|
||||||
|
asm.lines().size shouldBe 4
|
||||||
|
val opt = VmPeepholeOptimizer(asm)
|
||||||
|
opt.optimize()
|
||||||
|
val lines = asm.lines()
|
||||||
|
lines.size shouldBe 4
|
||||||
|
(lines[0] as VmCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||||
|
(lines[1] as VmCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||||
|
(lines[2] as VmCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||||
|
(lines[3] as VmCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||||
|
(lines[0] as VmCodeInstruction).ins.value shouldBe 0
|
||||||
|
(lines[1] as VmCodeInstruction).ins.value shouldBe 0
|
||||||
|
(lines[2] as VmCodeInstruction).ins.value shouldBe 255
|
||||||
|
(lines[3] as VmCodeInstruction).ins.value shouldBe 65535
|
||||||
|
}
|
||||||
|
})
|
@ -22,6 +22,9 @@ class BinExprSplitter(private val program: Program, private val options: Compila
|
|||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
if(options.compTarget.name == VMTarget.NAME)
|
||||||
|
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
|
||||||
|
|
||||||
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@ class ConstExprEvaluator {
|
|||||||
"&" -> bitwiseand(left, right)
|
"&" -> bitwiseand(left, right)
|
||||||
"|" -> bitwiseor(left, right)
|
"|" -> bitwiseor(left, right)
|
||||||
"^" -> bitwisexor(left, right)
|
"^" -> bitwisexor(left, right)
|
||||||
"and" -> logicaland(left, right)
|
|
||||||
"or" -> logicalor(left, right)
|
|
||||||
"xor" -> logicalxor(left, right)
|
|
||||||
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
||||||
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
||||||
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
||||||
@ -58,57 +55,6 @@ class ConstExprEvaluator {
|
|||||||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
|
||||||
val error = "cannot compute $left locical-bitxor $right"
|
|
||||||
return when (left.type) {
|
|
||||||
in IntegerDatatypes -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number != 0.0), left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number.toInt() != 0), left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number != 0.0), left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
|
||||||
val error = "cannot compute $left locical-or $right"
|
|
||||||
return when (left.type) {
|
|
||||||
in IntegerDatatypes -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number != 0.0, left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number.toInt() != 0, left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number != 0.0, left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
|
||||||
val error = "cannot compute $left locical-and $right"
|
|
||||||
return when (left.type) {
|
|
||||||
in IntegerDatatypes -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number != 0.0, left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> when (right.type) {
|
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number.toInt() != 0, left.position)
|
|
||||||
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number != 0.0, left.position)
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
|
@ -2,10 +2,10 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ExpressionError
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.UndefinedSymbolError
|
import prog8.ast.base.UndefinedSymbolError
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.maySwapOperandOrder
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.ast.statements.VarDeclType
|
import prog8.ast.statements.VarDeclType
|
||||||
@ -13,7 +13,6 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.AssociativeOperators
|
import prog8.code.core.AssociativeOperators
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.IntegerDatatypes
|
|
||||||
|
|
||||||
|
|
||||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
@ -35,59 +34,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// Try to turn a unary prefix expression into a single constant value.
|
val constValue = expr.constValue(program) ?: return noModifications
|
||||||
// Compile-time constant sub expressions will be evaluated on the spot.
|
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
|
||||||
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
|
||||||
val subexpr = expr.expression
|
|
||||||
if (subexpr is NumericLiteral) {
|
|
||||||
// accept prefixed literal values (such as -3, not true)
|
|
||||||
return when (expr.operator) {
|
|
||||||
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
|
|
||||||
"-" -> when (subexpr.type) {
|
|
||||||
in IntegerDatatypes -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral.optimalInteger(-subexpr.number.toInt(), subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral(DataType.FLOAT, -subexpr.number, subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
|
||||||
}
|
|
||||||
"~" -> when (subexpr.type) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
|
||||||
}
|
|
||||||
"not" -> {
|
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
|
||||||
NumericLiteral.fromBoolean(subexpr.number == 0.0, subexpr.position),
|
|
||||||
parent))
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(expr.operator, subexpr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -192,6 +140,21 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
|
||||||
|
if (expr.operator == "-") {
|
||||||
|
// X - -1 ---> X + 1
|
||||||
|
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||||
|
val plusExpr = BinaryExpression(expr.left, "+", posNumber, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||||
|
}
|
||||||
|
else if (expr.operator == "+") {
|
||||||
|
// X + -1 ---> X - 1
|
||||||
|
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||||
|
val plusExpr = BinaryExpression(expr.left, "-", posNumber, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val leftBinExpr = expr.left as? BinaryExpression
|
val leftBinExpr = expr.left as? BinaryExpression
|
||||||
val rightBinExpr = expr.right as? BinaryExpression
|
val rightBinExpr = expr.right as? BinaryExpression
|
||||||
@ -302,7 +265,6 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// the args of a fuction are constfolded via recursion already.
|
|
||||||
val constvalue = functionCallExpr.constValue(program)
|
val constvalue = functionCallExpr.constValue(program)
|
||||||
return if(constvalue!=null)
|
return if(constvalue!=null)
|
||||||
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
|
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
|
||||||
@ -310,6 +272,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
val constvalue = bfc.constValue(program)
|
||||||
|
return if(constvalue!=null)
|
||||||
|
listOf(IAstModification.ReplaceNode(bfc, constvalue, parent))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
|
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
|
||||||
val fromCast = rangeFrom.cast(targetDt)
|
val fromCast = rangeFrom.cast(targetDt)
|
||||||
@ -423,7 +393,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
// both operators are the same.
|
// both operators are the same.
|
||||||
|
|
||||||
// If associative, we can simply shuffle the const operands around to optimize.
|
// If associative, we can simply shuffle the const operands around to optimize.
|
||||||
if(expr.operator in AssociativeOperators) {
|
if(expr.operator in AssociativeOperators && maySwapOperandOrder(expr)) {
|
||||||
return if(leftIsConst) {
|
return if(leftIsConst) {
|
||||||
if(subleftIsConst)
|
if(subleftIsConst)
|
||||||
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IStatementContainer
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.maySwapOperandOrder
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||||
|
|
||||||
class ExpressionSimplifier(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
class ExpressionSimplifier(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
|
|
||||||
@ -24,8 +28,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
val literal = typecast.expression as? NumericLiteral
|
val literal = typecast.expression as? NumericLiteral
|
||||||
if (literal != null) {
|
if (literal != null) {
|
||||||
val newLiteral = literal.cast(typecast.type)
|
val newLiteral = literal.cast(typecast.type)
|
||||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove redundant nested typecasts
|
// remove redundant nested typecasts
|
||||||
@ -67,6 +72,22 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(compTarget.name!=VMTarget.NAME) {
|
||||||
|
val booleanCondition = ifElse.condition as? BinaryExpression
|
||||||
|
if(booleanCondition!=null && booleanCondition.operator=="&") {
|
||||||
|
// special optimization of WORD & $ff00 -> just and the msb of WORD with $ff
|
||||||
|
val rightNum = booleanCondition.right as? NumericLiteral
|
||||||
|
if(rightNum!=null && rightNum.type==DataType.UWORD && (rightNum.number.toInt() and 0x00ff)==0) {
|
||||||
|
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
|
||||||
|
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
|
||||||
|
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +101,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||||
|
|
||||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null)
|
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
|
||||||
return listOf(IAstModification.SwapOperands(expr))
|
return listOf(IAstModification.SwapOperands(expr))
|
||||||
|
|
||||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||||
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||||
if(parent !is Assignment || !(expr.left isSameAs parent.target))
|
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
|
||||||
return listOf(IAstModification.SwapOperands(expr))
|
return listOf(IAstModification.SwapOperands(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,35 +208,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// boolvar & 1 --> boolvar
|
||||||
|
// boolvar & 2 --> false
|
||||||
|
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||||
|
if(rightVal?.number==1.0) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||||
|
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// simplify when a term is constant and directly determines the outcome
|
// simplify when a term is constant and directly determines the outcome
|
||||||
val constTrue = NumericLiteral.fromBoolean(true, expr.position)
|
|
||||||
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
|
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
|
||||||
val newExpr: Expression? = when (expr.operator) {
|
val newExpr: Expression? = when (expr.operator) {
|
||||||
"or" -> {
|
|
||||||
when {
|
|
||||||
leftVal != null && leftVal.asBooleanValue || rightVal != null && rightVal.asBooleanValue -> constTrue
|
|
||||||
leftVal != null && !leftVal.asBooleanValue -> expr.right
|
|
||||||
rightVal != null && !rightVal.asBooleanValue -> expr.left
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"and" -> {
|
|
||||||
when {
|
|
||||||
leftVal != null && !leftVal.asBooleanValue || rightVal != null && !rightVal.asBooleanValue -> constFalse
|
|
||||||
leftVal != null && leftVal.asBooleanValue -> expr.right
|
|
||||||
rightVal != null && rightVal.asBooleanValue -> expr.left
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"xor" -> {
|
|
||||||
when {
|
|
||||||
leftVal != null && !leftVal.asBooleanValue -> expr.right
|
|
||||||
rightVal != null && !rightVal.asBooleanValue -> expr.left
|
|
||||||
leftVal != null && leftVal.asBooleanValue -> PrefixExpression("not", expr.right, expr.right.position)
|
|
||||||
rightVal != null && rightVal.asBooleanValue -> PrefixExpression("not", expr.left, expr.left.position)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"|" -> {
|
"|" -> {
|
||||||
when {
|
when {
|
||||||
leftVal?.number==0.0 -> expr.right
|
leftVal?.number==0.0 -> expr.right
|
||||||
@ -259,6 +264,28 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(rightVal!=null && leftDt==DataType.BOOL) {
|
||||||
|
// see if we can replace comparison against true/1 with simpler comparison against zero
|
||||||
|
if (expr.operator == "==") {
|
||||||
|
if (rightVal.number == 1.0) {
|
||||||
|
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.SetExpression({expr.operator="!="}, expr, parent),
|
||||||
|
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expr.operator == "!=") {
|
||||||
|
if (rightVal.number == 1.0) {
|
||||||
|
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.SetExpression({expr.operator="=="}, expr, parent),
|
||||||
|
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(newExpr != null)
|
if(newExpr != null)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
|
||||||
@ -277,6 +304,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if(arg is IdentifierReference && arg.nameInSource.size==2
|
||||||
|
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
|
||||||
|
// lsb(cx16.r0) -> cx16.r0L
|
||||||
|
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'L'), arg.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
|
||||||
|
}
|
||||||
val argDt = arg.inferType(program)
|
val argDt = arg.inferType(program)
|
||||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||||
// useless lsb() of byte value
|
// useless lsb() of byte value
|
||||||
@ -298,6 +331,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if(arg is IdentifierReference && arg.nameInSource.size==2
|
||||||
|
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
|
||||||
|
// msb(cx16.r0) -> cx16.r0H
|
||||||
|
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'H'), arg.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
|
||||||
|
}
|
||||||
val argDt = arg.inferType(program)
|
val argDt = arg.inferType(program)
|
||||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||||
// useless msb() of byte value, replace with 0
|
// useless msb() of byte value, replace with 0
|
||||||
@ -309,49 +348,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
|
||||||
|
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
|
||||||
|
// just cast the lsb to uword
|
||||||
|
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
|
||||||
val range = containment.iterable as? RangeExpression
|
|
||||||
if(range!=null && range.step.constValue(program)?.number==1.0) {
|
|
||||||
val from = range.from.constValue(program)
|
|
||||||
val to = range.to.constValue(program)
|
|
||||||
val value = containment.element
|
|
||||||
if(from!=null && to!=null && value.isSimple) {
|
|
||||||
if(to.number-from.number>6.0) {
|
|
||||||
// replace containment test with X>=from and X<=to
|
|
||||||
val left = BinaryExpression(value, ">=", from, containment.position)
|
|
||||||
val right = BinaryExpression(value.copy(), "<=", to, containment.position)
|
|
||||||
val comparison = BinaryExpression(left, "and", right, containment.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(containment, comparison, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(pipeExpr: PipeExpression, parent: Node) = processPipe(pipeExpr, parent)
|
|
||||||
override fun after(pipe: Pipe, parent: Node) = processPipe(pipe, parent)
|
|
||||||
|
|
||||||
private fun processPipe(pipe: IPipe, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(pipe.source.isSimple) {
|
|
||||||
val segments = pipe.segments
|
|
||||||
if(segments.size==1) {
|
|
||||||
// replace the whole pipe with a normal function call
|
|
||||||
val funcname = (segments[0] as IFunctionCall).target
|
|
||||||
val call = if(pipe is Pipe)
|
|
||||||
FunctionCallStatement(funcname, mutableListOf(pipe.source), true, pipe.position)
|
|
||||||
else
|
|
||||||
FunctionCallExpression(funcname, mutableListOf(pipe.source), pipe.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipe as Node, call, parent))
|
|
||||||
} else if(segments.size>1) {
|
|
||||||
// replace source+firstsegment by firstsegment(source) call as the new source
|
|
||||||
val firstSegment = segments.removeAt(0) as IFunctionCall
|
|
||||||
val call = FunctionCallExpression(firstSegment.target, mutableListOf(pipe.source), pipe.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipe.source, call, pipe as Node))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,19 +491,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
in powersOfTwo -> {
|
in powersOfTwo -> {
|
||||||
if (leftDt in IntegerDatatypes) {
|
if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
|
||||||
// divided by a power of two => shift right
|
// Unsigned number divided by a power of two => shift right
|
||||||
|
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values),
|
||||||
|
// so we leave that as is and let the code generator deal with it.
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in negativePowersOfTwo -> {
|
|
||||||
if (leftDt in IntegerDatatypes) {
|
|
||||||
// divided by a negative power of two => negate, then shift right
|
|
||||||
val numshifts = log2(-cv).toInt()
|
|
||||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftDt == DataType.UBYTE) {
|
if (leftDt == DataType.UBYTE) {
|
||||||
@ -557,9 +556,10 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
in negativePowersOfTwo -> {
|
in negativePowersOfTwo -> {
|
||||||
if (leftValue.inferType(program).isInteger) {
|
if (leftValue.inferType(program).isInteger) {
|
||||||
// times a negative power of two => negate, then shift left
|
// times a negative power of two => negate, then shift
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
val negation = PrefixExpression("-", expr2.left, expr.position)
|
||||||
|
return BinaryExpression(negation, "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,7 +589,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
if (amount >= 16) {
|
if (amount >= 16) {
|
||||||
return NumericLiteral(targetDt, 0.0, expr.position)
|
return NumericLiteral(targetDt, 0.0, expr.position)
|
||||||
} else if (amount > 8) {
|
}
|
||||||
|
else if(amount==8) {
|
||||||
|
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
|
||||||
|
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||||
|
}
|
||||||
|
else if (amount > 8) {
|
||||||
|
// same as above but with residual shifts.
|
||||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||||
@ -628,12 +635,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
if (amount >= 16) {
|
if (amount >= 16) {
|
||||||
return NumericLiteral.optimalInteger(0, expr.position)
|
return NumericLiteral.optimalInteger(0, expr.position)
|
||||||
}
|
}
|
||||||
|
else if(amount==8) {
|
||||||
|
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||||
|
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||||
|
}
|
||||||
else if (amount > 8) {
|
else if (amount > 8) {
|
||||||
|
// same as above but with residual shifts.
|
||||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
|
// bit-shifting a signed value shouldn't be allowed by the compiler but here we go...
|
||||||
if (amount > 16) {
|
if (amount > 16) {
|
||||||
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
|
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
|
||||||
return null
|
return null
|
||||||
@ -646,7 +660,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteral?): BinExprWithConstants {
|
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteral?): BinExprWithConstants {
|
||||||
if (expr.operator in AssociativeOperators && leftVal != null) {
|
if (expr.operator in AssociativeOperators && leftVal != null && maySwapOperandOrder(expr)) {
|
||||||
// swap left and right so that right is always the constant
|
// swap left and right so that right is always the constant
|
||||||
val tmp = expr.left
|
val tmp = expr.left
|
||||||
expr.left = expr.right
|
expr.left = expr.right
|
||||||
|
@ -60,8 +60,8 @@ fun Program.inlineSubroutines(): Int {
|
|||||||
return inliner.applyModifications()
|
return inliner.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
fun Program.simplifyExpressions(target: ICompilationTarget) : Int {
|
||||||
val opti = ExpressionSimplifier(this, errors)
|
val opti = ExpressionSimplifier(this, target)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ import prog8.code.core.InternalCompilerException
|
|||||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
||||||
|
|
||||||
|
|
||||||
|
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||||
|
|
||||||
class Inliner(val program: Program): AstWalker() {
|
class Inliner(val program: Program): AstWalker() {
|
||||||
|
|
||||||
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||||
@ -104,7 +106,7 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
} else
|
} else
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
is Jump, is GoSub -> true
|
is Jump -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,8 +144,10 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
val scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,43 +170,63 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
return super.before(program)
|
return super.before(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = gosub.identifier.targetStatement(program) as? Subroutine
|
if(sub.inline && sub.parameters.isEmpty()) {
|
||||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
|
||||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
||||||
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
|
||||||
listOf(IAstModification.ReplaceNode(gosub, 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.
|
||||||
when (val toInline = sub.statements.first()) {
|
when (val toInline = sub.statements.first()) {
|
||||||
is Return -> noModifications
|
is Return -> {
|
||||||
else -> listOf(IAstModification.ReplaceNode(gosub, toInline.copy(), parent))
|
val fcall = toInline.value as? FunctionCallExpression
|
||||||
|
if(fcall!=null) {
|
||||||
|
// insert the function call expression as a void function call directly
|
||||||
|
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||||
|
} else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||||
|
return if(sub==null)
|
||||||
|
noModifications
|
||||||
|
else
|
||||||
|
possibleInlineFcallStmt(sub, functionCallStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||||
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
||||||
return if(sub.isAsmSubroutine) {
|
return if(sub.isAsmSubroutine) {
|
||||||
// simply insert the asm for the argument-less routine
|
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
|
||||||
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
|
noModifications
|
||||||
} else {
|
} else {
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
|
||||||
when (val toInline = sub.statements.first()) {
|
when (val toInline = sub.statements.first()) {
|
||||||
is Return -> noModifications
|
is Return -> {
|
||||||
else -> listOf(IAstModification.ReplaceNode(functionCallStatement, toInline.copy(), parent))
|
// 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.
|
||||||
|
if(toInline.value!=null)
|
||||||
|
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
else -> noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO also inline function call expressions, and remove it from the StatementOptimizer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,46 +16,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
private val compTarget: ICompilationTarget
|
private val compTarget: ICompilationTarget
|
||||||
) : AstWalker() {
|
) : AstWalker() {
|
||||||
|
|
||||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
// if the first instruction in the called subroutine is a return statement with a simple value (NOT being a parameter),
|
|
||||||
// remove the jump altogeter and inline the returnvalue directly. (only if not part of a pipe expression)
|
|
||||||
|
|
||||||
fun scopePrefix(variable: IdentifierReference): IdentifierReference {
|
|
||||||
val target = variable.targetStatement(program) as INamedStatement
|
|
||||||
return IdentifierReference(target.scopedName, variable.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
val subroutine = functionCallExpr.target.targetSubroutine(program)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Return && first.value?.isSimple==true && parent !is IPipe) {
|
|
||||||
val copy = when(val orig = first.value!!) {
|
|
||||||
is AddressOf -> {
|
|
||||||
val scoped = scopePrefix(orig.identifier)
|
|
||||||
AddressOf(scoped, orig.position)
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
when(val expr = orig.addressExpression) {
|
|
||||||
is NumericLiteral -> DirectMemoryRead(expr.copy(), orig.position)
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
if(orig.targetVarDecl(program)?.origin == VarDeclOrigin.SUBROUTINEPARAM)
|
|
||||||
return noModifications
|
|
||||||
else
|
|
||||||
scopePrefix(orig)
|
|
||||||
}
|
|
||||||
is NumericLiteral -> orig.copy()
|
|
||||||
is StringLiteral -> orig.copy()
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, copy, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.size==1) {
|
if(functionCallStatement.target.nameInSource.size==1) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
@ -113,31 +73,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
|
||||||
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Return)
|
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(compTarget.name!=VMTarget.NAME) {
|
|
||||||
// see if we can optimize any complex argument expressions to be just a simple variable
|
|
||||||
// TODO for now, only works for single-argument functions because we use just 1 temp var: R9
|
|
||||||
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
|
|
||||||
val arg = functionCallStatement.args[0]
|
|
||||||
if(!arg.isSimple && arg !is IFunctionCall) {
|
|
||||||
val name = getTempRegisterName(arg.inferType(program))
|
|
||||||
val tempvar = IdentifierReference(name, functionCallStatement.position)
|
|
||||||
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.InsertBefore(functionCallStatement, assignTempvar, parent as IStatementContainer),
|
|
||||||
IAstModification.ReplaceNode(arg, tempvar, functionCallStatement)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +83,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
// empty true part? switch with the else part
|
// empty true part? switch with the else part
|
||||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||||
val invertedCondition = PrefixExpression("not", ifElse.condition, ifElse.condition.position)
|
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
|
||||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
@ -284,25 +219,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NOTE: do NOT remove a jump to the next statement, because this will lead to wrong code when this occurs at the end of a subroutine
|
|
||||||
// if we want to optimize this away, it can be done later at code generation time.
|
|
||||||
|
|
||||||
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
|
||||||
// if the next statement is return with no returnvalue, change into a regular jump if there are no parameters as well.
|
|
||||||
val subroutineParams = gosub.identifier.targetSubroutine(program)?.parameters
|
|
||||||
if(subroutineParams!=null && subroutineParams.isEmpty()) {
|
|
||||||
val returnstmt = gosub.nextSibling() as? Return
|
|
||||||
if(returnstmt!=null && returnstmt.value==null) {
|
|
||||||
return listOf(
|
|
||||||
IAstModification.Remove(returnstmt, parent as IStatementContainer),
|
|
||||||
IAstModification.ReplaceNode(gosub, Jump(null, gosub.identifier, null, gosub.position), parent)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
@ -313,7 +229,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
val op1 = binExpr.operator
|
val op1 = binExpr.operator
|
||||||
val op2 = rExpr.operator
|
val op2 = rExpr.operator
|
||||||
|
|
||||||
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators) {
|
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators && maySwapOperandOrder(binExpr)) {
|
||||||
// associative operator, make sure the constant numeric value is second (right)
|
// associative operator, make sure the constant numeric value is second (right)
|
||||||
return listOf(IAstModification.SwapOperands(rExpr))
|
return listOf(IAstModification.SwapOperands(rExpr))
|
||||||
}
|
}
|
||||||
@ -352,7 +268,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(binExpr.operator in AssociativeOperators && binExpr.right isSameAs assignment.target) {
|
if(binExpr.operator in AssociativeOperators && binExpr.right isSameAs assignment.target) {
|
||||||
// associative operator, swap the operands so that the assignment target is first (left)
|
// associative operator, swap the operands so that the assignment target is first (left)
|
||||||
// unless the other operand is the same in which case we don't swap (endless loop!)
|
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||||
if (!(binExpr.left isSameAs binExpr.right))
|
if (!(binExpr.left isSameAs binExpr.right) && maySwapOperandOrder(binExpr))
|
||||||
return listOf(IAstModification.SwapOperands(binExpr))
|
return listOf(IAstModification.SwapOperands(binExpr))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,31 +385,22 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(compTarget.name==VMTarget.NAME)
|
if(compTarget.name==VMTarget.NAME)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
val returnvalue = returnStmt.value
|
||||||
val subr = returnStmt.definingSubroutine!!
|
if (returnvalue!=null) {
|
||||||
val returnDt = subr.returntypes.single()
|
val dt = returnvalue.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if (returnDt in IntegerDatatypes) {
|
if(dt!=DataType.UNDEFINED) {
|
||||||
|
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
|
||||||
// first assign to intermediary variable, then return that
|
// first assign to intermediary variable, then return that
|
||||||
val (returnVarName, _) = program.getTempVar(returnDt)
|
val (returnVarName, _) = program.getTempVar(dt)
|
||||||
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
||||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
||||||
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
val assign = Assignment(tgt, returnvalue, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
||||||
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO decision when to use intermediary variable to calculate returnvalue seems a bit arbitrary...
|
|
||||||
val returnvalue = returnStmt.value
|
|
||||||
if (returnvalue!=null) {
|
|
||||||
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
|
|
||||||
val mod = returnViaIntermediaryVar(returnvalue)
|
|
||||||
if(mod!=null)
|
|
||||||
return mod
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.BinaryExpression
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.expressions.PrefixExpression
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
@ -28,23 +31,23 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
|
|
||||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(breakStmt)
|
reportUnreachable(breakStmt)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(jump)
|
reportUnreachable(jump)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(returnStmt)
|
reportUnreachable(returnStmt)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||||
reportUnreachable(functionCallStatement)
|
reportUnreachable(functionCallStatement)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportUnreachable(stmt: Statement) {
|
private fun reportUnreachable(stmt: Statement) {
|
||||||
@ -236,7 +239,6 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
is PrefixExpression,
|
is PrefixExpression,
|
||||||
is BinaryExpression,
|
is BinaryExpression,
|
||||||
is TypecastExpression,
|
is TypecastExpression,
|
||||||
is PipeExpression,
|
|
||||||
is IFunctionCall -> { /* don't remove */ }
|
is IFunctionCall -> { /* don't remove */ }
|
||||||
else -> {
|
else -> {
|
||||||
if(assign1.value !is IFunctionCall)
|
if(assign1.value !is IFunctionCall)
|
||||||
|
@ -38,9 +38,9 @@ dependencies {
|
|||||||
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.4'
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
@ -68,7 +68,6 @@ sourceSets {
|
|||||||
test {
|
test {
|
||||||
java {
|
java {
|
||||||
srcDir "${project.projectDir}/test"
|
srcDir "${project.projectDir}/test"
|
||||||
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
; Prog8 definitions for the Atari800XL
|
; Prog8 definitions for the Atari800XL
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
|
|
||||||
atari {
|
atari {
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
|
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Prog8 definitions for floating point handling on the Commodore 128
|
; Prog8 definitions for floating point handling on the Commodore 128
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
%import floats_functions
|
%import floats_functions
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
; Prog8 definitions for the Commodore-128
|
; Prog8 definitions for the Commodore-128
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
|
|
||||||
@ -725,6 +722,7 @@ cx16 {
|
|||||||
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
|
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
|
||||||
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
|
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
|
||||||
; you're doing insane nesting of expressions...)
|
; you're doing insane nesting of expressions...)
|
||||||
|
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||||
&uword r0 = $1b00
|
&uword r0 = $1b00
|
||||||
&uword r1 = $1b02
|
&uword r1 = $1b02
|
||||||
&uword r2 = $1b04
|
&uword r2 = $1b04
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Prog8 definitions for floating point handling on the Commodore-64
|
; Prog8 definitions for floating point handling on the Commodore-64
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
%import floats_functions
|
%import floats_functions
|
||||||
@ -13,7 +9,6 @@ floats {
|
|||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
float tempvar_swap_float ; used for some swap() operations
|
|
||||||
|
|
||||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||||
|
|
||||||
|
@ -38,8 +38,12 @@ graphics {
|
|||||||
|
|
||||||
if y1>y2 {
|
if y1>y2 {
|
||||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||||
swap(x1, x2)
|
cx16.r0 = x1
|
||||||
swap(y1, y2)
|
x1 = x2
|
||||||
|
x2 = cx16.r0
|
||||||
|
cx16.r0L = y1
|
||||||
|
y1 = y2
|
||||||
|
y2 = cx16.r0L
|
||||||
}
|
}
|
||||||
word @zp dx = (x2 as word)-x1
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = (y2 as word)-y1
|
word @zp dy = (y2 as word)-y1
|
||||||
@ -311,7 +315,7 @@ hline_zero2
|
|||||||
; for efficiency of internal algorithms here is the internal plot routine
|
; for efficiency of internal algorithms here is the internal plot routine
|
||||||
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
||||||
|
|
||||||
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
uword @zp internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
||||||
|
|
||||||
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
; Prog8 definitions for the Commodore-64
|
; Prog8 definitions for the Commodore-64
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
@ -689,6 +685,7 @@ cx16 {
|
|||||||
; (because there's no room for them in the zeropage)
|
; (because there's no room for them in the zeropage)
|
||||||
; they are allocated at the bottom of the eval-stack (should be ample space unless
|
; they are allocated at the bottom of the eval-stack (should be ample space unless
|
||||||
; you're doing insane nesting of expressions...)
|
; you're doing insane nesting of expressions...)
|
||||||
|
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||||
&uword r0 = $cf00
|
&uword r0 = $cf00
|
||||||
&uword r1 = $cf02
|
&uword r1 = $cf02
|
||||||
&uword r2 = $cf04
|
&uword r2 = $cf04
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
; Number conversions routines.
|
; Number conversions routines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
|
|
||||||
conv {
|
conv {
|
||||||
|
|
||||||
@ -419,6 +416,9 @@ _loop
|
|||||||
beq _stop
|
beq _stop
|
||||||
cmp #7 ; screencode letters A-F are 1-6
|
cmp #7 ; screencode letters A-F are 1-6
|
||||||
bcc _add_letter
|
bcc _add_letter
|
||||||
|
and #127
|
||||||
|
cmp #97
|
||||||
|
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
|
||||||
cmp #'g'
|
cmp #'g'
|
||||||
bcs _stop
|
bcs _stop
|
||||||
cmp #'a'
|
cmp #'a'
|
||||||
@ -454,6 +454,11 @@ _add_letter
|
|||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
pla
|
pla
|
||||||
jmp _calc
|
jmp _calc
|
||||||
|
_try_iso
|
||||||
|
cmp #103
|
||||||
|
bcs _stop
|
||||||
|
and #63
|
||||||
|
bne _add_letter
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Cx16 specific disk drive I/O routines.
|
; Cx16 specific disk drive I/O routines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%import diskio
|
%import diskio
|
||||||
|
|
||||||
@ -73,4 +71,102 @@ cx16diskio {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; replacement function that makes use of fast block read capability of the X16
|
||||||
|
; use this in place of regular diskio.f_read()
|
||||||
|
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||||
|
; -- read from the currently open file, up to the given number of bytes.
|
||||||
|
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||||
|
if not diskio.iteration_in_progress or not num_bytes
|
||||||
|
return 0
|
||||||
|
|
||||||
|
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||||
|
if diskio.have_first_byte {
|
||||||
|
diskio.have_first_byte=false
|
||||||
|
@(bufferpointer) = diskio.first_byte
|
||||||
|
bufferpointer++
|
||||||
|
diskio.list_blocks++
|
||||||
|
num_bytes--
|
||||||
|
}
|
||||||
|
|
||||||
|
void c64.CHKIN(11) ; use #11 as input channel again
|
||||||
|
|
||||||
|
; commander X16 supports fast block-read via macptr() kernal call
|
||||||
|
uword size
|
||||||
|
while num_bytes {
|
||||||
|
size = 255
|
||||||
|
if num_bytes<size
|
||||||
|
size = num_bytes
|
||||||
|
size = cx16.macptr(lsb(size), bufferpointer)
|
||||||
|
if_cs
|
||||||
|
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||||
|
diskio.list_blocks += size
|
||||||
|
bufferpointer += size
|
||||||
|
if msb(bufferpointer) == $c0
|
||||||
|
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||||
|
num_bytes -= size
|
||||||
|
if c64.READST() & $40 {
|
||||||
|
diskio.f_close() ; end of file, close it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diskio.list_blocks ; number of bytes read
|
||||||
|
|
||||||
|
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||||
|
%asm {{
|
||||||
|
lda bufferpointer
|
||||||
|
sta m_in_buffer+1
|
||||||
|
lda bufferpointer+1
|
||||||
|
sta m_in_buffer+2
|
||||||
|
}}
|
||||||
|
repeat num_bytes {
|
||||||
|
%asm {{
|
||||||
|
jsr c64.CHRIN
|
||||||
|
sta cx16.r5
|
||||||
|
m_in_buffer sta $ffff
|
||||||
|
inc m_in_buffer+1
|
||||||
|
bne +
|
||||||
|
inc m_in_buffer+2
|
||||||
|
+ inc diskio.list_blocks
|
||||||
|
bne +
|
||||||
|
inc diskio.list_blocks+1
|
||||||
|
+
|
||||||
|
}}
|
||||||
|
|
||||||
|
if cx16.r5==$0d { ; chance on I/o error status?
|
||||||
|
diskio.first_byte = c64.READST()
|
||||||
|
if diskio.first_byte & $40 {
|
||||||
|
diskio.f_close() ; end of file, close it
|
||||||
|
diskio.list_blocks-- ; don't count that last CHRIN read
|
||||||
|
}
|
||||||
|
if diskio.first_byte
|
||||||
|
return diskio.list_blocks ; number of bytes read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diskio.list_blocks ; number of bytes read
|
||||||
|
}
|
||||||
|
|
||||||
|
; replacement function that makes use of fast block read capability of the X16
|
||||||
|
; use this in place of regular diskio.f_read_all()
|
||||||
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
|
; -- read the full contents of the file, returns number of bytes read.
|
||||||
|
if not diskio.iteration_in_progress
|
||||||
|
return 0
|
||||||
|
|
||||||
|
uword total_read = 0
|
||||||
|
if diskio.have_first_byte {
|
||||||
|
diskio.have_first_byte=false
|
||||||
|
@(bufferpointer) = diskio.first_byte
|
||||||
|
bufferpointer++
|
||||||
|
total_read = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while not c64.READST() {
|
||||||
|
uword size = cx16diskio.f_read(bufferpointer, 256)
|
||||||
|
total_read += size
|
||||||
|
bufferpointer += size
|
||||||
|
}
|
||||||
|
return total_read
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Prog8 definitions for floating point handling on the CommanderX16
|
; Prog8 definitions for floating point handling on the CommanderX16
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
%import floats_functions
|
%import floats_functions
|
||||||
|
@ -28,7 +28,7 @@ gfx2 {
|
|||||||
uword width = 0
|
uword width = 0
|
||||||
uword height = 0
|
uword height = 0
|
||||||
ubyte bpp = 0
|
ubyte bpp = 0
|
||||||
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
bool monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
||||||
|
|
||||||
sub screen_mode(ubyte mode) {
|
sub screen_mode(ubyte mode) {
|
||||||
when mode {
|
when mode {
|
||||||
@ -127,7 +127,7 @@ gfx2 {
|
|||||||
position(0, 0)
|
position(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
sub monochrome_stipple(ubyte enable) {
|
sub monochrome_stipple(bool enable) {
|
||||||
monochrome_dont_stipple_flag = not enable
|
monochrome_dont_stipple_flag = not enable
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +394,12 @@ _done
|
|||||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||||
if y1>y2 {
|
if y1>y2 {
|
||||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||||
swap(x1, x2)
|
cx16.r0 = x1
|
||||||
swap(y1, y2)
|
x1 = x2
|
||||||
|
x2 = cx16.r0
|
||||||
|
cx16.r0 = y1
|
||||||
|
y1 = y2
|
||||||
|
y2 = cx16.r0
|
||||||
}
|
}
|
||||||
word @zp dx = (x2 as word)-x1
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = (y2 as word)-y1
|
word @zp dy = (y2 as word)-y1
|
||||||
@ -679,7 +683,7 @@ _done
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub position2(uword @zp x, uword y, ubyte also_port_1) {
|
sub position2(uword @zp x, uword y, bool also_port_1) {
|
||||||
position(x, y)
|
position(x, y)
|
||||||
if also_port_1 {
|
if also_port_1 {
|
||||||
when active_mode {
|
when active_mode {
|
||||||
|
@ -2,15 +2,20 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
; Bitmap pixel graphics module for the CommanderX16
|
; Bitmap pixel graphics module for the CommanderX16
|
||||||
; wraps the graphics functions that are in ROM.
|
; Wraps the graphics functions that are in ROM.
|
||||||
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
|
; Only lo-res 320x240 256 color mode for now.
|
||||||
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
; Unlike graphics module on the C64, you can use colors() to set new drawing colors for every draw operation.
|
||||||
|
; For other resolutions or other color modes, use the "gfx2" module instead. (which is Cx16-specific)
|
||||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||||
|
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
const uword WIDTH = 320
|
const uword WIDTH = 320
|
||||||
const ubyte HEIGHT = 200
|
const ubyte HEIGHT = 240
|
||||||
|
|
||||||
|
|
||||||
|
ubyte stroke_color = 1
|
||||||
|
ubyte background_color = 0
|
||||||
|
|
||||||
sub enable_bitmap_mode() {
|
sub enable_bitmap_mode() {
|
||||||
; enable bitmap screen, erase it and set colors to black/white.
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
@ -27,10 +32,18 @@ graphics {
|
|||||||
|
|
||||||
|
|
||||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
|
stroke_color = pixelcolor
|
||||||
|
background_color = bgcolor
|
||||||
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||||
cx16.GRAPH_clear()
|
cx16.GRAPH_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub colors(ubyte stroke, ubyte fill) {
|
||||||
|
; this routine is only available on the cx16, other targets can't change colors on the fly
|
||||||
|
cx16.GRAPH_set_colors(stroke, fill, background_color)
|
||||||
|
stroke_color = stroke
|
||||||
|
}
|
||||||
|
|
||||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||||
}
|
}
|
||||||
@ -71,31 +84,31 @@ graphics {
|
|||||||
cx16.r0 = xcenter + xx
|
cx16.r0 = xcenter + xx
|
||||||
cx16.r1 = ycenter + yy
|
cx16.r1 = ycenter + yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - xx
|
cx16.r0 = xcenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + xx
|
cx16.r0 = xcenter + xx
|
||||||
cx16.r1 = ycenter - yy
|
cx16.r1 = ycenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - xx
|
cx16.r0 = xcenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + yy
|
cx16.r0 = xcenter + yy
|
||||||
cx16.r1 = ycenter + xx
|
cx16.r1 = ycenter + xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - yy
|
cx16.r0 = xcenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + yy
|
cx16.r0 = xcenter + yy
|
||||||
cx16.r1 = ycenter - xx
|
cx16.r1 = ycenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - yy
|
cx16.r0 = xcenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
yy++
|
yy++
|
||||||
if decisionOver2<=0 {
|
if decisionOver2<=0 {
|
||||||
decisionOver2 += (yy as word)*2+1
|
decisionOver2 += (yy as word)*2+1
|
||||||
|
@ -13,20 +13,20 @@ palette {
|
|||||||
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
sub set_rgb_be(uword palette_ptr, uword num_colors) {
|
||||||
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
|
; 1 word per color entry, $0rgb in big endian format
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat num_colors {
|
repeat num_colors {
|
||||||
cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
|
cx16.vpoke(1, vera_palette_ptr+1, @(palette_ptr))
|
||||||
palette_bytes_ptr++
|
palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
|
cx16.vpoke(1, vera_palette_ptr, @(palette_ptr))
|
||||||
palette_bytes_ptr++
|
palette_ptr++
|
||||||
vera_palette_ptr+=2
|
vera_palette_ptr+=2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_rgb(uword palette_words_ptr, uword num_colors) {
|
sub set_rgb(uword palette_words_ptr, uword num_colors) {
|
||||||
; 1 word per color entry (in little endian format so $gb0r)
|
; 1 word per color entry (in little endian format as layed out in video memory, so $gb0r)
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat num_colors*2 {
|
repeat num_colors*2 {
|
||||||
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
|
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
|
||||||
|
157
compiler/res/prog8lib/cx16/psg.p8
Normal file
157
compiler/res/prog8lib/cx16/psg.p8
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
%import syslib
|
||||||
|
|
||||||
|
psg {
|
||||||
|
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
|
||||||
|
; 00 frequency word LSB
|
||||||
|
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
|
||||||
|
; 02 bit 7 =right, bit 6 = left, bits 5-0 = volume 0-63 levels
|
||||||
|
; 03 bit 7,6 = waveform, bits 5-0 = Pulse width 0-63
|
||||||
|
; waveform: 0=pulse, 1=sawtooth, 2=triangle, 3=noise
|
||||||
|
const ubyte PULSE = %00000000
|
||||||
|
const ubyte SAWTOOTH = %01000000
|
||||||
|
const ubyte TRIANGLE = %10000000
|
||||||
|
const ubyte NOISE = %11000000
|
||||||
|
const ubyte LEFT = %01000000
|
||||||
|
const ubyte RIGHT = %10000000
|
||||||
|
|
||||||
|
sub voice(ubyte voice_num, ubyte channel, ubyte volume, ubyte waveform, ubyte pulsewidth) {
|
||||||
|
envelope_states[voice_num] = 255
|
||||||
|
cx16.r0 = $f9c2 + voice_num * 4
|
||||||
|
cx16.VERA_CTRL = 0
|
||||||
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
|
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||||
|
cx16.VERA_ADDR_H = 1
|
||||||
|
cx16.VERA_DATA0 = channel | volume
|
||||||
|
cx16.VERA_ADDR_L++
|
||||||
|
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||||
|
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||||
|
envelope_maxvolumes[voice_num] = volume
|
||||||
|
}
|
||||||
|
|
||||||
|
; sub freq_hz(ubyte voice_num, float hertz) {
|
||||||
|
; ; this would rely on floating point math to convert hertz to vera frequency
|
||||||
|
; ; TODO should be replaced by integer math maybe with a lookup table?
|
||||||
|
; uword vera_freq = (hertz / 0.3725290298461914) as uword
|
||||||
|
; freq(voice_num, vera_freq)
|
||||||
|
; }
|
||||||
|
|
||||||
|
sub freq(ubyte voice_num, uword vera_freq) {
|
||||||
|
cx16.r0 = $f9c0 + voice_num * 4
|
||||||
|
cx16.VERA_CTRL = 0
|
||||||
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
|
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||||
|
cx16.VERA_ADDR_H = 1
|
||||||
|
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||||
|
cx16.VERA_ADDR_L++
|
||||||
|
cx16.VERA_DATA0 = msb(vera_freq)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume(ubyte voice_num, ubyte vol) {
|
||||||
|
cx16.r0 = $f9c2 + voice_num * 4
|
||||||
|
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | vol)
|
||||||
|
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||||
|
envelope_maxvolumes[voice_num] = vol
|
||||||
|
}
|
||||||
|
|
||||||
|
sub pulse_width(ubyte voice_num, ubyte pw) {
|
||||||
|
cx16.r0 = $f9c3 + voice_num * 4
|
||||||
|
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
|
||||||
|
envelope_states[voice_num] = 255
|
||||||
|
envelope_attacks[voice_num] = attack
|
||||||
|
envelope_sustains[voice_num] = sustain
|
||||||
|
envelope_releases[voice_num] = release
|
||||||
|
if attack
|
||||||
|
attack = 0
|
||||||
|
else
|
||||||
|
attack = maxvolume ; max volume when no attack is set
|
||||||
|
envelope_volumes[voice_num] = mkword(attack, 0)
|
||||||
|
envelope_maxvolumes[voice_num] = maxvolume
|
||||||
|
envelope_states[voice_num] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sub silent() {
|
||||||
|
for cx16.r1L in 0 to 15 {
|
||||||
|
envelope_states[cx16.r1L] = 255
|
||||||
|
envelope_volumes[cx16.r1L] = 0
|
||||||
|
volume(cx16.r1L, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub envelopes_irq() {
|
||||||
|
; If you want to use real-time volume envelopes (Attack-Sustain-Release),
|
||||||
|
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
|
||||||
|
; or just install this routine as the only irq handler if you don't have to do other things there.
|
||||||
|
; Example: cx16.set_irq(&psg.envelopes_irq, true)
|
||||||
|
|
||||||
|
; cx16.r0 = the volume word (volume scaled by 256)
|
||||||
|
; cx16.r1L = the voice number
|
||||||
|
; cx16.r2L = attack value
|
||||||
|
|
||||||
|
pushw(cx16.r0)
|
||||||
|
push(cx16.r1L)
|
||||||
|
push(cx16.r2L)
|
||||||
|
pushw(cx16.r9)
|
||||||
|
; calculate new volumes
|
||||||
|
for cx16.r1L in 0 to 15 {
|
||||||
|
when envelope_states[cx16.r1L] {
|
||||||
|
0 -> {
|
||||||
|
; attack
|
||||||
|
cx16.r2L = envelope_maxvolumes[cx16.r1L]
|
||||||
|
cx16.r0 = envelope_volumes[cx16.r1L] + envelope_attacks[cx16.r1L] * $0040
|
||||||
|
if msb(cx16.r0) > cx16.r2L or envelope_attacks[cx16.r1L]==0 {
|
||||||
|
cx16.r0 = mkword(cx16.r2L, 0)
|
||||||
|
envelope_attacks[cx16.r1L] = 0
|
||||||
|
envelope_states[cx16.r1L] = 1 ; start sustain
|
||||||
|
}
|
||||||
|
envelope_volumes[cx16.r1L] = cx16.r0
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
; sustain
|
||||||
|
if envelope_sustains[cx16.r1L] {
|
||||||
|
envelope_sustains[cx16.r1L]--
|
||||||
|
} else {
|
||||||
|
envelope_states[cx16.r1L] = 2 ; start release
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
; release
|
||||||
|
cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040
|
||||||
|
if msb(cx16.r0) & %11000000 {
|
||||||
|
cx16.r0 = 0
|
||||||
|
envelope_releases[cx16.r1L] = 0
|
||||||
|
}
|
||||||
|
envelope_volumes[cx16.r1L] = cx16.r0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; set new volumes of all 16 voices, using vera stride of 4
|
||||||
|
cx16.push_vera_context()
|
||||||
|
cx16.VERA_CTRL = 0
|
||||||
|
cx16.VERA_ADDR_L = $c2
|
||||||
|
cx16.VERA_ADDR_M = $f9
|
||||||
|
cx16.VERA_ADDR_H = 1 | %00110000
|
||||||
|
cx16.VERA_CTRL = 1
|
||||||
|
cx16.VERA_ADDR_L = $c2
|
||||||
|
cx16.VERA_ADDR_M = $f9
|
||||||
|
cx16.VERA_ADDR_H = 1 | %00110000
|
||||||
|
for cx16.r1L in 0 to 15 {
|
||||||
|
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||||
|
}
|
||||||
|
cx16.pop_vera_context()
|
||||||
|
popw(cx16.r9)
|
||||||
|
pop(cx16.r2L)
|
||||||
|
pop(cx16.r1L)
|
||||||
|
popw(cx16.r0)
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte[16] envelope_states
|
||||||
|
uword[16] envelope_volumes ; scaled by 256
|
||||||
|
ubyte[16] envelope_attacks
|
||||||
|
ubyte[16] envelope_sustains
|
||||||
|
ubyte[16] envelope_releases
|
||||||
|
ubyte[16] envelope_maxvolumes
|
||||||
|
}
|
@ -1,9 +1,5 @@
|
|||||||
; Prog8 definitions for the CommanderX16
|
; Prog8 definitions for the CommanderX16
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
|
|
||||||
@ -102,7 +98,7 @@ cx16 {
|
|||||||
&uword ISTOP = $0328
|
&uword ISTOP = $0328
|
||||||
&uword IGETIN = $032a
|
&uword IGETIN = $032a
|
||||||
&uword ICLALL = $032c
|
&uword ICLALL = $032c
|
||||||
&uword KEYHDL = $032e ; keyboard scan code handler
|
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
|
||||||
&uword ILOAD = $0330
|
&uword ILOAD = $0330
|
||||||
&uword ISAVE = $0332
|
&uword ISAVE = $0332
|
||||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||||
@ -310,7 +306,7 @@ romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
|||||||
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
|
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
|
||||||
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||||
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||||
romsub $ff6e = jsrfar()
|
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||||
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||||
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||||
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||||
@ -367,7 +363,7 @@ romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
|||||||
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||||
|
|
||||||
romsub $ff44 = macptr() clobbers(A,X,Y)
|
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY) clobbers(A) -> ubyte @Pc, uword @XY
|
||||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||||
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
||||||
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
||||||
@ -415,28 +411,28 @@ asmsub mouse_pos() -> ubyte @A {
|
|||||||
inline asmsub rombank(ubyte bank @A) {
|
inline asmsub rombank(ubyte bank @A) {
|
||||||
; -- set the rom banks
|
; -- set the rom banks
|
||||||
%asm {{
|
%asm {{
|
||||||
sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
sta $01
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub rambank(ubyte bank @A) {
|
inline asmsub rambank(ubyte bank @A) {
|
||||||
; -- set the ram bank
|
; -- set the ram bank
|
||||||
%asm {{
|
%asm {{
|
||||||
sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
sta $00
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub getrombank() -> ubyte @A {
|
inline asmsub getrombank() -> ubyte @A {
|
||||||
; -- get the current rom bank
|
; -- get the current rom bank
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
lda $01
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub getrambank() -> ubyte @A {
|
inline asmsub getrambank() -> ubyte @A {
|
||||||
; -- get the current ram bank
|
; -- get the current ram bank
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
lda $00
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,8 +639,12 @@ asmsub init_system_phase2() {
|
|||||||
|
|
||||||
asmsub cleanup_at_exit() {
|
asmsub cleanup_at_exit() {
|
||||||
; executed when the main subroutine does rts
|
; executed when the main subroutine does rts
|
||||||
; just an rts here, nothing special to clean up on cx16
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
lda #1
|
||||||
|
sta $00 ; ram bank 1
|
||||||
|
lda #4
|
||||||
|
sta $01 ; rom bank 4 (kernal)
|
||||||
|
stz $2d ; hack to reset machine code monitor bank to 0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -735,6 +735,56 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub push_vera_context() clobbers(A) {
|
||||||
|
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||||
|
%asm {{
|
||||||
|
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||||
|
lda cx16.VERA_ADDR_L
|
||||||
|
sta _vera_storage
|
||||||
|
lda cx16.VERA_ADDR_M
|
||||||
|
sta _vera_storage+1
|
||||||
|
lda cx16.VERA_ADDR_H
|
||||||
|
sta _vera_storage+2
|
||||||
|
lda cx16.VERA_CTRL
|
||||||
|
sta _vera_storage+3
|
||||||
|
eor #1
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.VERA_ADDR_L
|
||||||
|
sta _vera_storage+4
|
||||||
|
lda cx16.VERA_ADDR_M
|
||||||
|
sta _vera_storage+5
|
||||||
|
lda cx16.VERA_ADDR_H
|
||||||
|
sta _vera_storage+6
|
||||||
|
lda cx16.VERA_CTRL
|
||||||
|
sta _vera_storage+7
|
||||||
|
rts
|
||||||
|
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub pop_vera_context() clobbers(A) {
|
||||||
|
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||||
|
%asm {{
|
||||||
|
lda cx16.push_vera_context._vera_storage+7
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.push_vera_context._vera_storage+6
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.push_vera_context._vera_storage+5
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.push_vera_context._vera_storage+4
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
lda cx16.push_vera_context._vera_storage+3
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.push_vera_context._vera_storage+2
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.push_vera_context._vera_storage+1
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.push_vera_context._vera_storage+0
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
asmsub restore_irq() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -819,6 +869,8 @@ 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)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; C64 and Cx16 disk drive I/O routines.
|
; C64 and Cx16 disk drive I/O routines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%import string
|
%import string
|
||||||
@ -8,11 +6,12 @@
|
|||||||
|
|
||||||
diskio {
|
diskio {
|
||||||
|
|
||||||
sub directory(ubyte drivenumber) -> ubyte {
|
sub directory(ubyte drivenumber) -> bool {
|
||||||
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
c64.SETNAM(1, "$")
|
||||||
c64.SETLFS(13, drivenumber, 0)
|
c64.SETLFS(13, drivenumber, 0)
|
||||||
|
ubyte status = 1
|
||||||
void c64.OPEN() ; open 13,8,0,"$"
|
void c64.OPEN() ; open 13,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
@ -25,8 +24,13 @@ diskio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
; while not key pressed / EOF encountered, read data.
|
; while not key pressed / EOF encountered, read data.
|
||||||
ubyte status = c64.READST()
|
status = c64.READST()
|
||||||
while not status {
|
if status!=0 {
|
||||||
|
status = 1
|
||||||
|
goto io_error
|
||||||
|
}
|
||||||
|
|
||||||
|
while status==0 {
|
||||||
ubyte low = c64.CHRIN()
|
ubyte low = c64.CHRIN()
|
||||||
ubyte high = c64.CHRIN()
|
ubyte high = c64.CHRIN()
|
||||||
txt.print_uw(mkword(high, low))
|
txt.print_uw(mkword(high, low))
|
||||||
@ -45,9 +49,9 @@ diskio {
|
|||||||
if c64.STOP2()
|
if c64.STOP2()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
status = c64.READST()
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
status = c64.READST()
|
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(13)
|
c64.CLOSE(13)
|
||||||
|
|
||||||
@ -61,34 +65,69 @@ io_error:
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub diskname(ubyte drivenumber) -> uword {
|
||||||
|
; -- Returns pointer to disk name string or 0 if failure.
|
||||||
|
|
||||||
|
c64.SETNAM(1, "$")
|
||||||
|
c64.SETLFS(13, drivenumber, 0)
|
||||||
|
ubyte okay = false
|
||||||
|
void c64.OPEN() ; open 13,8,0,"$"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void c64.CHKIN(13) ; use #13 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
repeat 6 {
|
||||||
|
void c64.CHRIN() ; skip the 6 prologue bytes
|
||||||
|
}
|
||||||
|
if c64.READST()!=0
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
; while not key pressed / EOF encountered, read data.
|
||||||
|
cx16.r0 = &list_filename
|
||||||
|
repeat {
|
||||||
|
@(cx16.r0) = c64.CHRIN()
|
||||||
|
if @(cx16.r0)==0
|
||||||
|
break
|
||||||
|
cx16.r0++
|
||||||
|
}
|
||||||
|
okay = true
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
|
c64.CLOSE(13)
|
||||||
|
if okay
|
||||||
|
return &list_filename
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
; internal variables for the iterative file lister / loader
|
; internal variables for the iterative file lister / loader
|
||||||
ubyte list_skip_disk_name
|
bool list_skip_disk_name
|
||||||
uword list_pattern
|
uword list_pattern
|
||||||
uword list_blocks
|
uword list_blocks
|
||||||
ubyte iteration_in_progress = false
|
bool iteration_in_progress = false
|
||||||
ubyte @zp first_byte
|
ubyte @zp first_byte
|
||||||
ubyte have_first_byte
|
bool have_first_byte
|
||||||
str list_filename = "?" * 32
|
str list_filename = "?" * 50
|
||||||
|
|
||||||
|
|
||||||
; ----- get a list of files (uses iteration functions internally) -----
|
; ----- get a list of files (uses iteration functions internally) -----
|
||||||
|
|
||||||
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
|
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
|
||||||
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
|
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested. Returns number of files.
|
||||||
const uword names_buf_size = 800
|
const uword filenames_buf_size = 800
|
||||||
uword names_buffer = memory("filenames", names_buf_size, 0)
|
uword filenames_buffer = memory("filenames", filenames_buf_size, 0)
|
||||||
uword buffer_start = names_buffer
|
uword buffer_start = filenames_buffer
|
||||||
ubyte files_found = 0
|
ubyte files_found = 0
|
||||||
if lf_start_list(drivenumber, pattern_ptr) {
|
if lf_start_list(drivenumber, pattern_ptr) {
|
||||||
while lf_next_entry() {
|
while lf_next_entry() {
|
||||||
@(name_ptrs) = lsb(names_buffer)
|
@(name_ptrs) = lsb(filenames_buffer)
|
||||||
name_ptrs++
|
name_ptrs++
|
||||||
@(name_ptrs) = msb(names_buffer)
|
@(name_ptrs) = msb(filenames_buffer)
|
||||||
name_ptrs++
|
name_ptrs++
|
||||||
names_buffer += string.copy(diskio.list_filename, names_buffer) + 1
|
filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1
|
||||||
files_found++
|
files_found++
|
||||||
if names_buffer - buffer_start > names_buf_size-18
|
if filenames_buffer - buffer_start > filenames_buf_size-18
|
||||||
break
|
break
|
||||||
if files_found == max_names
|
if files_found == max_names
|
||||||
break
|
break
|
||||||
@ -100,7 +139,7 @@ io_error:
|
|||||||
|
|
||||||
; ----- iterative file lister functions (uses io channel 12) -----
|
; ----- iterative file lister functions (uses io channel 12) -----
|
||||||
|
|
||||||
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> ubyte {
|
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool {
|
||||||
; -- start an iterative file listing with optional pattern matching.
|
; -- start an iterative file listing with optional pattern matching.
|
||||||
; note: only a single iteration loop can be active at a time!
|
; note: only a single iteration loop can be active at a time!
|
||||||
lf_end_list()
|
lf_end_list()
|
||||||
@ -129,7 +168,7 @@ io_error:
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sub lf_next_entry() -> ubyte {
|
sub lf_next_entry() -> bool {
|
||||||
; -- retrieve the next entry from an iterative file listing session.
|
; -- retrieve the next entry from an iterative file listing session.
|
||||||
; results will be found in list_blocks and list_filename.
|
; results will be found in list_blocks and list_filename.
|
||||||
; if it returns false though, there are no more entries (or an error occurred).
|
; if it returns false though, there are no more entries (or an error occurred).
|
||||||
@ -201,7 +240,7 @@ close_end:
|
|||||||
|
|
||||||
; ----- iterative file loader functions (uses io channel 11) -----
|
; ----- iterative file loader functions (uses io channel 11) -----
|
||||||
|
|
||||||
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
|
sub f_open(ubyte drivenumber, uword filenameptr) -> bool {
|
||||||
; -- open a file for iterative reading with f_read
|
; -- open a file for iterative reading with f_read
|
||||||
; note: only a single iteration loop can be active at a time!
|
; note: only a single iteration loop can be active at a time!
|
||||||
f_close()
|
f_close()
|
||||||
@ -210,6 +249,7 @@ close_end:
|
|||||||
c64.SETLFS(11, drivenumber, 0)
|
c64.SETLFS(11, drivenumber, 0)
|
||||||
void c64.OPEN() ; open 11,8,0,"filename"
|
void c64.OPEN() ; open 11,8,0,"filename"
|
||||||
if_cc {
|
if_cc {
|
||||||
|
if c64.READST()==0 {
|
||||||
iteration_in_progress = true
|
iteration_in_progress = true
|
||||||
have_first_byte = false
|
have_first_byte = false
|
||||||
void c64.CHKIN(11) ; use #11 as input channel
|
void c64.CHKIN(11) ; use #11 as input channel
|
||||||
@ -221,6 +261,7 @@ close_end:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
f_close()
|
f_close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -228,6 +269,8 @@ close_end:
|
|||||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||||
; -- read from the currently open file, up to the given number of bytes.
|
; -- read from the currently open file, up to the given number of bytes.
|
||||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||||
|
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
|
||||||
|
; automatically load into subsequent banks if it reaches a bank boundary!
|
||||||
if not iteration_in_progress or not num_bytes
|
if not iteration_in_progress or not num_bytes
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -241,6 +284,7 @@ close_end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void c64.CHKIN(11) ; use #11 as input channel again
|
void c64.CHKIN(11) ; use #11 as input channel again
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda bufferpointer
|
lda bufferpointer
|
||||||
sta m_in_buffer+1
|
sta m_in_buffer+1
|
||||||
@ -263,13 +307,15 @@ m_in_buffer sta $ffff
|
|||||||
|
|
||||||
if cx16.r5==$0d { ; chance on I/o error status?
|
if cx16.r5==$0d { ; chance on I/o error status?
|
||||||
first_byte = c64.READST()
|
first_byte = c64.READST()
|
||||||
if first_byte & $40
|
if first_byte & $40 {
|
||||||
f_close() ; end of file, close it
|
f_close() ; end of file, close it
|
||||||
|
list_blocks-- ; don't count that last CHRIN read
|
||||||
|
}
|
||||||
if first_byte
|
if first_byte
|
||||||
return list_blocks
|
return list_blocks ; number of bytes read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list_blocks
|
return list_blocks ; number of bytes read
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
@ -277,19 +323,20 @@ m_in_buffer sta $ffff
|
|||||||
if not iteration_in_progress
|
if not iteration_in_progress
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
uword total_read = 0
|
||||||
if have_first_byte {
|
if have_first_byte {
|
||||||
have_first_byte=false
|
have_first_byte=false
|
||||||
@(bufferpointer) = first_byte
|
@(bufferpointer) = first_byte
|
||||||
bufferpointer++
|
bufferpointer++
|
||||||
list_blocks++
|
total_read = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
while not c64.READST() {
|
while not c64.READST() {
|
||||||
list_blocks += f_read(bufferpointer, 256)
|
uword size = f_read(bufferpointer, 256)
|
||||||
bufferpointer += 256
|
total_read += size
|
||||||
|
bufferpointer += size
|
||||||
}
|
}
|
||||||
return list_blocks
|
return total_read
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
||||||
@ -339,7 +386,7 @@ _end rts
|
|||||||
|
|
||||||
; ----- iterative file saver functions (uses io channel 14) -----
|
; ----- iterative file saver functions (uses io channel 14) -----
|
||||||
|
|
||||||
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte {
|
sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
|
||||||
; -- open a file for iterative writing with f_write
|
; -- open a file for iterative writing with f_write
|
||||||
f_close_w()
|
f_close_w()
|
||||||
|
|
||||||
@ -354,7 +401,7 @@ _end rts
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte {
|
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||||
; -- write the given umber of bytes to the currently open file
|
; -- write the given umber of bytes to the currently open file
|
||||||
if num_bytes!=0 {
|
if num_bytes!=0 {
|
||||||
void c64.CHKOUT(14) ; use #14 as input channel again
|
void c64.CHKOUT(14) ; use #14 as input channel again
|
||||||
@ -378,8 +425,8 @@ _end rts
|
|||||||
|
|
||||||
sub status(ubyte drivenumber) -> uword {
|
sub status(ubyte drivenumber) -> uword {
|
||||||
; -- retrieve the disk drive's current status message
|
; -- retrieve the disk drive's current status message
|
||||||
uword messageptr = &filename
|
uword messageptr = &list_filename
|
||||||
c64.SETNAM(0, filename)
|
c64.SETNAM(0, list_filename)
|
||||||
c64.SETLFS(15, drivenumber, 15)
|
c64.SETLFS(15, drivenumber, 15)
|
||||||
void c64.OPEN() ; open 15,8,15
|
void c64.OPEN() ; open 15,8,15
|
||||||
if_cs
|
if_cs
|
||||||
@ -389,22 +436,25 @@ _end rts
|
|||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
while not c64.READST() {
|
while not c64.READST() {
|
||||||
@(messageptr) = c64.CHRIN()
|
first_byte = c64.CHRIN()
|
||||||
|
if first_byte=='\r' or first_byte=='\n'
|
||||||
|
break
|
||||||
|
@(messageptr) = first_byte
|
||||||
messageptr++
|
messageptr++
|
||||||
}
|
}
|
||||||
|
|
||||||
@(messageptr) = 0
|
@(messageptr) = 0
|
||||||
|
|
||||||
done:
|
done:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
c64.CLOSE(15)
|
||||||
return filename
|
return list_filename
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
filename = "?disk error"
|
list_filename = "?disk error"
|
||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
c64.SETLFS(1, drivenumber, 0)
|
||||||
uword @shared end_address = address + size
|
uword @shared end_address = address + size
|
||||||
@ -474,7 +524,7 @@ io_error:
|
|||||||
|
|
||||||
; Internal routine, only to be used on Commander X16 platform if headerless=true,
|
; Internal routine, only to be used on Commander X16 platform if headerless=true,
|
||||||
; because this routine uses kernal support for that to load headerless files.
|
; because this routine uses kernal support for that to load headerless files.
|
||||||
sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, ubyte headerless) -> uword {
|
sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
ubyte secondary = 1
|
ubyte secondary = 1
|
||||||
cx16.r1 = 0
|
cx16.r1 = 0
|
||||||
@ -502,10 +552,10 @@ io_error:
|
|||||||
|
|
||||||
sub delete(ubyte drivenumber, uword filenameptr) {
|
sub delete(ubyte drivenumber, uword filenameptr) {
|
||||||
; -- delete a file on the drive
|
; -- delete a file on the drive
|
||||||
filename[0] = 's'
|
list_filename[0] = 's'
|
||||||
filename[1] = ':'
|
list_filename[1] = ':'
|
||||||
ubyte flen = string.copy(filenameptr, &filename+2)
|
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
||||||
c64.SETNAM(flen+2, filename)
|
c64.SETNAM(flen+2, list_filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void c64.OPEN()
|
||||||
c64.CLRCHN()
|
c64.CLRCHN()
|
||||||
@ -514,17 +564,24 @@ io_error:
|
|||||||
|
|
||||||
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
||||||
; -- rename a file on the drive
|
; -- rename a file on the drive
|
||||||
filename[0] = 'r'
|
list_filename[0] = 'r'
|
||||||
filename[1] = ':'
|
list_filename[1] = ':'
|
||||||
ubyte flen_new = string.copy(newfileptr, &filename+2)
|
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
||||||
filename[flen_new+2] = '='
|
list_filename[flen_new+2] = '='
|
||||||
ubyte flen_old = string.copy(oldfileptr, &filename+3+flen_new)
|
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
||||||
c64.SETNAM(3+flen_new+flen_old, filename)
|
c64.SETNAM(3+flen_new+flen_old, list_filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void c64.OPEN()
|
||||||
c64.CLRCHN()
|
c64.CLRCHN()
|
||||||
c64.CLOSE(1)
|
c64.CLOSE(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
str filename = "0:??????????????????????????????????????"
|
sub send_command(ubyte drivenumber, uword commandptr) {
|
||||||
|
; -- send a dos command to the drive
|
||||||
|
c64.SETNAM(string.length(commandptr), commandptr)
|
||||||
|
c64.SETLFS(15, drivenumber, 15)
|
||||||
|
void c64.OPEN()
|
||||||
|
c64.CLRCHN()
|
||||||
|
c64.CLOSE(15)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
; Internal Math library routines - always included by the compiler
|
; Internal Math library routines - always included by the compiler
|
||||||
; Generic machine independent 6502 code.
|
; Generic machine independent 6502 code.
|
||||||
;
|
;
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; some more interesting routines can be found here:
|
; some more interesting routines can be found here:
|
||||||
; http://6502org.wikidot.com/software-math
|
; http://6502org.wikidot.com/software-math
|
||||||
; http://codebase64.org/doku.php?id=base:6502_6510_maths
|
; http://codebase64.org/doku.php?id=base:6502_6510_maths
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Internal Math library routines - always included by the compiler
|
; Internal Math library routines - always included by the compiler
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
math {
|
math {
|
||||||
%asminclude "library:math.asm"
|
%asminclude "library:math.asm"
|
||||||
|
@ -86,9 +86,9 @@ func_all_w_stack .proc
|
|||||||
|
|
||||||
abs_b_stack .proc
|
abs_b_stack .proc
|
||||||
; -- push abs(A) on stack (as unsigned word)
|
; -- push abs(A) on stack (as unsigned word)
|
||||||
jsr abs_b_into_A
|
jsr abs_b_into_AY
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
stz p8ESTACK_HI,x
|
stz P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
; Internal library routines - always included by the compiler
|
; Internal library routines - always included by the compiler
|
||||||
; Generic machine independent 6502 code.
|
; Generic machine independent 6502 code.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
|
|
||||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||||
@ -60,27 +58,6 @@ inv_word .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
not_byte .proc
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
not_word .proc
|
|
||||||
lda P8ESTACK_LO + 1,x
|
|
||||||
ora P8ESTACK_HI + 1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor #1
|
|
||||||
sta P8ESTACK_LO + 1,x
|
|
||||||
lsr a
|
|
||||||
sta P8ESTACK_HI + 1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
bitand_b .proc
|
bitand_b .proc
|
||||||
; -- bitwise and (of 2 bytes)
|
; -- bitwise and (of 2 bytes)
|
||||||
lda P8ESTACK_LO+2,x
|
lda P8ESTACK_LO+2,x
|
||||||
@ -144,98 +121,6 @@ bitxor_w .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
and_b .proc
|
|
||||||
; -- logical and (of 2 bytes)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ sta P8ZP_SCRATCH_B1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ and P8ZP_SCRATCH_B1
|
|
||||||
inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
or_b .proc
|
|
||||||
; -- logical or (of 2 bytes)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
ora P8ESTACK_LO+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
xor_b .proc
|
|
||||||
; -- logical xor (of 2 bytes)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ sta P8ZP_SCRATCH_B1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor P8ZP_SCRATCH_B1
|
|
||||||
inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
and_w .proc
|
|
||||||
; -- logical and (word and word -> byte)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
ora P8ESTACK_HI+2,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ sta P8ZP_SCRATCH_B1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ora P8ESTACK_HI+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ and P8ZP_SCRATCH_B1
|
|
||||||
inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
sta P8ESTACK_HI+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
or_w .proc
|
|
||||||
; -- logical or (word or word -> byte)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
ora P8ESTACK_LO+1,x
|
|
||||||
ora P8ESTACK_HI+2,x
|
|
||||||
ora P8ESTACK_HI+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
sta P8ESTACK_HI+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
xor_w .proc
|
|
||||||
; -- logical xor (word xor word -> byte)
|
|
||||||
lda P8ESTACK_LO+2,x
|
|
||||||
ora P8ESTACK_HI+2,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ sta P8ZP_SCRATCH_B1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ora P8ESTACK_HI+1,x
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+ eor P8ZP_SCRATCH_B1
|
|
||||||
inx
|
|
||||||
sta P8ESTACK_LO+1,x
|
|
||||||
sta P8ESTACK_HI+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_w .proc
|
add_w .proc
|
||||||
; -- push word+word / uword+uword
|
; -- push word+word / uword+uword
|
||||||
@ -780,13 +665,10 @@ greaterequalzero_sb .proc
|
|||||||
|
|
||||||
greaterequalzero_sw .proc
|
greaterequalzero_sw .proc
|
||||||
lda P8ESTACK_HI+1,x
|
lda P8ESTACK_HI+1,x
|
||||||
|
bpl equalzero_b._true
|
||||||
bmi equalzero_b._false
|
bmi equalzero_b._false
|
||||||
ora P8ESTACK_LO+1,x
|
|
||||||
beq equalzero_b._true
|
|
||||||
bne equalzero_b._false
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
memcopy16_up .proc
|
memcopy16_up .proc
|
||||||
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||||
; clobbers register A,X,Y
|
; clobbers register A,X,Y
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Internal library routines - always included by the compiler
|
; Internal library routines - always included by the compiler
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
prog8_lib {
|
prog8_lib {
|
||||||
%asminclude "library:prog8_lib.asm"
|
%asminclude "library:prog8_lib.asm"
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
; 0-terminated string manipulation routines.
|
; 0-terminated string manipulation routines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
|
|
||||||
string {
|
string {
|
||||||
|
|
||||||
@ -132,7 +129,7 @@ _startloop dey
|
|||||||
|
|
||||||
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
|
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
|
||||||
; Locates the first position of the given character in the string,
|
; Locates the first position of the given character in the string,
|
||||||
; returns Carry set if found + index in A, or Carry clear if not found.
|
; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
|
||||||
%asm {{
|
%asm {{
|
||||||
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
|
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
@ -147,7 +144,8 @@ _startloop dey
|
|||||||
beq _found
|
beq _found
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
_notfound clc
|
_notfound lda #0
|
||||||
|
clc
|
||||||
rts
|
rts
|
||||||
_found tya
|
_found tya
|
||||||
sec
|
sec
|
||||||
@ -226,6 +224,26 @@ _done rts
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub startswith(str st, str prefix) -> bool {
|
||||||
|
ubyte prefix_len = length(prefix)
|
||||||
|
ubyte str_len = length(st)
|
||||||
|
if prefix_len > str_len
|
||||||
|
return false
|
||||||
|
cx16.r9L = st[prefix_len]
|
||||||
|
st[prefix_len] = 0
|
||||||
|
cx16.r9H = compare(st, prefix) as ubyte
|
||||||
|
st[prefix_len] = cx16.r9L
|
||||||
|
return cx16.r9H==0
|
||||||
|
}
|
||||||
|
|
||||||
|
sub endswith(str st, str suffix) -> bool {
|
||||||
|
ubyte suffix_len = length(suffix)
|
||||||
|
ubyte str_len = length(st)
|
||||||
|
if suffix_len > str_len
|
||||||
|
return false
|
||||||
|
return compare(st + str_len - suffix_len, suffix) == 0
|
||||||
|
}
|
||||||
|
|
||||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
||||||
%asm {{
|
%asm {{
|
||||||
; pattern matching of a string.
|
; pattern matching of a string.
|
||||||
@ -296,5 +314,4 @@ fail clc ; yes, no match found, return with c=0
|
|||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
; Number conversions routines.
|
; Number conversions routines.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
|
|
||||||
conv {
|
conv {
|
||||||
|
|
||||||
@ -254,4 +251,13 @@ sub bin2uword(str string) -> uword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub any2uword(str string) -> uword {
|
||||||
|
; -- convert any number string (any prefix allowed) to uword.
|
||||||
|
when string[0] {
|
||||||
|
'$' -> return hex2uword(string)
|
||||||
|
'%' -> return bin2uword(string)
|
||||||
|
else -> return str2uword(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Prog8 definitions for floating point handling on the VirtualMachine
|
; Prog8 definitions for floating point handling on the VirtualMachine
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
@ -86,7 +84,7 @@ sub log2(float value) -> float {
|
|||||||
sub sqrt(float value) -> float {
|
sub sqrt(float value) -> float {
|
||||||
%asm {{
|
%asm {{
|
||||||
loadm.f fr0,{floats.sqrt.value}
|
loadm.f fr0,{floats.sqrt.value}
|
||||||
fsqrt.f fr0,fr0
|
sqrt.f fr0,fr0
|
||||||
return
|
return
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Internal Math library routines - always included by the compiler
|
; Internal Math library routines - always included by the compiler
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
math {
|
math {
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Internal library routines - always included by the compiler
|
; Internal library routines - always included by the compiler
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
; 0-terminated string manipulation routines. For the Virtual Machine target.
|
; 0-terminated string manipulation routines. For the Virtual Machine target.
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
|
|
||||||
string {
|
string {
|
||||||
sub length(str st) -> ubyte {
|
sub length(str st) -> ubyte {
|
||||||
@ -116,4 +113,24 @@ string {
|
|||||||
ix++
|
ix++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub startswith(str st, str prefix) -> bool {
|
||||||
|
ubyte prefix_len = length(prefix)
|
||||||
|
ubyte str_len = length(st)
|
||||||
|
if prefix_len > str_len
|
||||||
|
return false
|
||||||
|
cx16.r9L = st[prefix_len]
|
||||||
|
st[prefix_len] = 0
|
||||||
|
cx16.r9H = compare(st, prefix) as ubyte
|
||||||
|
st[prefix_len] = cx16.r9L
|
||||||
|
return cx16.r9H==0
|
||||||
|
}
|
||||||
|
|
||||||
|
sub endswith(str st, str suffix) -> bool {
|
||||||
|
ubyte suffix_len = length(suffix)
|
||||||
|
ubyte str_len = length(st)
|
||||||
|
if suffix_len > str_len
|
||||||
|
return false
|
||||||
|
return compare(st + str_len - suffix_len, suffix) == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
; Prog8 definitions for the Virtual Machine
|
; Prog8 definitions for the Virtual Machine
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
|
|
||||||
sys {
|
sys {
|
||||||
; ------- lowlevel system routines --------
|
; ------- lowlevel system routines --------
|
||||||
@ -98,13 +95,21 @@ sys {
|
|||||||
syscall 10
|
syscall 10
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
|
||||||
|
%asm {{
|
||||||
|
loadm.w r0, {sys.gfx_getpixel.xx}
|
||||||
|
loadm.w r1, {sys.gfx_getpixel.yy}
|
||||||
|
syscall 30
|
||||||
|
return
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||||
; they are simulated on the VirtualMachine as well but their location in memory is different
|
; they are simulated on the VirtualMachine as well but their location in memory is different
|
||||||
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
|
||||||
&uword r0 = $0002
|
&uword r0 = $0002
|
||||||
&uword r1 = $0004
|
&uword r1 = $0004
|
||||||
&uword r2 = $0006
|
&uword r2 = $0006
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
|
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
|
|
||||||
%import conv
|
%import conv
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
8.1
|
8.4
|
||||||
|
@ -3,21 +3,18 @@ package prog8
|
|||||||
import kotlinx.cli.*
|
import kotlinx.cli.*
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.code.core.CbmPrgLauncherType
|
import prog8.code.core.CbmPrgLauncherType
|
||||||
|
import prog8.code.core.toHex
|
||||||
import prog8.code.target.*
|
import prog8.code.target.*
|
||||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.CompilationResult
|
||||||
import prog8.compiler.CompilerArguments
|
import prog8.compiler.CompilerArguments
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.*
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.nio.file.StandardWatchEventKinds
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
|
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
|
||||||
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
||||||
@ -46,10 +43,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')")
|
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
|
||||||
.default(C64Target.NAME)
|
|
||||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt listing in the VM instead")
|
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt listing in the VM instead")
|
||||||
|
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||||
|
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
|
||||||
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 {
|
||||||
@ -84,6 +82,27 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
return runVm(moduleFiles.first())
|
return runVm(moduleFiles.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var evalStackAddr: UInt? = null
|
||||||
|
if(evalStackAddrString!=null) {
|
||||||
|
try {
|
||||||
|
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
|
||||||
|
evalStackAddrString!!.substring(2).toUInt(16)
|
||||||
|
else if (evalStackAddrString!!.startsWith("$"))
|
||||||
|
evalStackAddrString!!.substring(1).toUInt(16)
|
||||||
|
else
|
||||||
|
evalStackAddrString!!.toUInt()
|
||||||
|
} catch(nx: NumberFormatException) {
|
||||||
|
System.err.println("invalid address for evalstack: $evalStackAddrString")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
|
||||||
|
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
|
||||||
|
|
||||||
if(watchMode==true) {
|
if(watchMode==true) {
|
||||||
val watchservice = FileSystems.getDefault().newWatchService()
|
val watchservice = FileSystems.getDefault().newWatchService()
|
||||||
val allImportedFiles = mutableSetOf<Path>()
|
val allImportedFiles = mutableSetOf<Path>()
|
||||||
@ -104,6 +123,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
|
evalStackAddr,
|
||||||
|
processedSymbols,
|
||||||
srcdirs,
|
srcdirs,
|
||||||
outputPath
|
outputPath
|
||||||
)
|
)
|
||||||
@ -124,16 +145,26 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
}
|
}
|
||||||
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
|
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
|
||||||
|
|
||||||
|
fun determineRecompilationNeeded(event: WatchKey): Boolean {
|
||||||
|
if(event.isValid) {
|
||||||
|
for (changed in event.pollEvents()) {
|
||||||
|
if (changed.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
|
||||||
|
val changedPath = changed.context() as Path
|
||||||
|
if (allImportedFiles.any { it.fileName == changedPath.fileName }) {
|
||||||
|
println(" change detected: $changedPath")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var recompile=false
|
var recompile=false
|
||||||
while(!recompile) {
|
while(!recompile) {
|
||||||
val event = watchservice.take()
|
val event = watchservice.take()
|
||||||
for (changed in event.pollEvents()) {
|
Thread.sleep(50) // avoid multiple events on same file
|
||||||
val changedPath = changed.context() as Path
|
recompile = determineRecompilationNeeded(event)
|
||||||
if(allImportedFiles.any { it.fileName == changedPath.fileName }) {
|
|
||||||
println(" change detected: $changedPath")
|
|
||||||
recompile = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event.reset()
|
event.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +187,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
|
evalStackAddr,
|
||||||
|
processedSymbols,
|
||||||
srcdirs,
|
srcdirs,
|
||||||
outputPath
|
outputPath
|
||||||
)
|
)
|
||||||
@ -193,6 +226,21 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
|
||||||
|
val result = mutableMapOf<String, String>()
|
||||||
|
val defPattern = """(.+)\s*=\s*(.+)""".toRegex()
|
||||||
|
for(def in symbolDefs) {
|
||||||
|
val match = defPattern.matchEntire(def.trim())
|
||||||
|
if(match==null) {
|
||||||
|
System.err.println("invalid symbol definition (expected NAME=VALUE): $def")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val (_, name, value) = match.groupValues
|
||||||
|
result[name.trim()] = value.trim()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
fun runVm(listingFilename: String): Boolean {
|
fun runVm(listingFilename: String): Boolean {
|
||||||
val name =
|
val name =
|
||||||
if(listingFilename.endsWith(".p8virt"))
|
if(listingFilename.endsWith(".p8virt"))
|
||||||
|
@ -38,6 +38,8 @@ class CompilerArguments(val filepath: Path,
|
|||||||
val asmListfile: Boolean,
|
val asmListfile: Boolean,
|
||||||
val experimentalCodegen: Boolean,
|
val experimentalCodegen: Boolean,
|
||||||
val compilationTarget: String,
|
val compilationTarget: String,
|
||||||
|
val evalStackBaseAddress: UInt?,
|
||||||
|
val symbolDefs: Map<String, String>,
|
||||||
val sourceDirs: List<String> = emptyList(),
|
val sourceDirs: List<String> = emptyList(),
|
||||||
val outputDir: Path = Path(""),
|
val outputDir: Path = Path(""),
|
||||||
val errors: IErrorReporter = ErrorReporter())
|
val errors: IErrorReporter = ErrorReporter())
|
||||||
@ -77,10 +79,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
asmQuiet = args.quietAssembler
|
asmQuiet = args.quietAssembler
|
||||||
asmListfile = args.asmListfile
|
asmListfile = args.asmListfile
|
||||||
experimentalCodegen = args.experimentalCodegen
|
experimentalCodegen = args.experimentalCodegen
|
||||||
|
evalStackBaseAddress = args.evalStackBaseAddress
|
||||||
outputDir = args.outputDir.normalize()
|
outputDir = args.outputDir.normalize()
|
||||||
|
symbolDefs = args.symbolDefs
|
||||||
}
|
}
|
||||||
program = programresult
|
program = programresult
|
||||||
importedFiles = imported
|
importedFiles = imported
|
||||||
|
|
||||||
|
if(compilationOptions.evalStackBaseAddress!=null) {
|
||||||
|
compTarget.machine.overrideEvalStack(compilationOptions.evalStackBaseAddress!!)
|
||||||
|
}
|
||||||
|
|
||||||
processAst(program, args.errors, compilationOptions)
|
processAst(program, args.errors, compilationOptions)
|
||||||
if (compilationOptions.optimize) {
|
if (compilationOptions.optimize) {
|
||||||
// println("*********** AST RIGHT BEFORE OPTIMIZING *************")
|
// println("*********** AST RIGHT BEFORE OPTIMIZING *************")
|
||||||
@ -319,7 +328,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
|||||||
|
|
||||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
println("Analyzing code...")
|
println("Analyzing code...")
|
||||||
program.preprocessAst(errors, compilerOptions.compTarget)
|
program.preprocessAst(errors, compilerOptions)
|
||||||
program.checkIdentifiers(errors, compilerOptions)
|
program.checkIdentifiers(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)
|
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)
|
||||||
@ -330,6 +339,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
|||||||
errors.report()
|
errors.report()
|
||||||
program.reorderStatements(errors, compilerOptions)
|
program.reorderStatements(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
|
program.changeNotExpression(errors)
|
||||||
|
errors.report()
|
||||||
program.addTypecasts(errors, compilerOptions)
|
program.addTypecasts(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
program.variousCleanups(errors, compilerOptions)
|
program.variousCleanups(errors, compilerOptions)
|
||||||
@ -347,7 +358,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
|||||||
remover.applyModifications()
|
remover.applyModifications()
|
||||||
while (true) {
|
while (true) {
|
||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = program.simplifyExpressions(errors)
|
val optsDone1 = program.simplifyExpressions(compTarget)
|
||||||
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
|
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
|
||||||
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
||||||
val optsDone4 = program.inlineSubroutines()
|
val optsDone4 = program.inlineSubroutines()
|
||||||
|
@ -6,7 +6,6 @@ import com.github.michaelbull.result.getOrElse
|
|||||||
import com.github.michaelbull.result.mapError
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.determineGosubArguments
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
@ -45,12 +44,10 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
is Directive -> transform(statement)
|
is Directive -> transform(statement)
|
||||||
is ForLoop -> transform(statement)
|
is ForLoop -> transform(statement)
|
||||||
is FunctionCallStatement -> transform(statement)
|
is FunctionCallStatement -> transform(statement)
|
||||||
is GoSub -> transform(statement)
|
|
||||||
is IfElse -> transform(statement)
|
is IfElse -> transform(statement)
|
||||||
is InlineAssembly -> transform(statement)
|
is InlineAssembly -> transform(statement)
|
||||||
is Jump -> transform(statement)
|
is Jump -> transform(statement)
|
||||||
is Label -> transform(statement)
|
is Label -> transform(statement)
|
||||||
is Pipe -> transform(statement)
|
|
||||||
is PostIncrDecr -> transform(statement)
|
is PostIncrDecr -> transform(statement)
|
||||||
is RepeatLoop -> transform(statement)
|
is RepeatLoop -> transform(statement)
|
||||||
is Return -> transform(statement)
|
is Return -> transform(statement)
|
||||||
@ -80,7 +77,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
is FunctionCallExpression -> transform(expr)
|
is FunctionCallExpression -> transform(expr)
|
||||||
is IdentifierReference -> transform(expr)
|
is IdentifierReference -> transform(expr)
|
||||||
is NumericLiteral -> transform(expr)
|
is NumericLiteral -> transform(expr)
|
||||||
is PipeExpression -> transform(expr)
|
|
||||||
is PrefixExpression -> transform(expr)
|
is PrefixExpression -> transform(expr)
|
||||||
is RangeExpression -> transform(expr)
|
is RangeExpression -> transform(expr)
|
||||||
is StringLiteral -> transform(expr)
|
is StringLiteral -> transform(expr)
|
||||||
@ -89,12 +85,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transform(srcAssign: Assignment): PtNode {
|
private fun transform(srcAssign: Assignment): PtNode {
|
||||||
if(srcAssign.origin==AssignmentOrigin.PARAMETERASSIGN) {
|
|
||||||
// assignments that are setting the parameters for a function call,
|
|
||||||
// will be gathered at the GoSub itself later.
|
|
||||||
return PtNop(srcAssign.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
val assign = PtAssignment(srcAssign.position)
|
val assign = PtAssignment(srcAssign.position)
|
||||||
assign.add(transform(srcAssign.target))
|
assign.add(transform(srcAssign.target))
|
||||||
assign.add(transformExpression(srcAssign.value))
|
assign.add(transformExpression(srcAssign.value))
|
||||||
@ -225,10 +215,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
|
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
|
||||||
val (target, _) = targetOf(srcCall.target)
|
val (target, _) = targetOf(srcCall.target)
|
||||||
val type = srcCall.inferType(program).getOrElse {
|
val type = srcCall.inferType(program).getOrElse {
|
||||||
if((srcCall.parent as? Pipe)?.segments?.last() === srcCall)
|
|
||||||
// for a pipe, the last segment is allowed to be a call to a function not returning anything.
|
|
||||||
DataType.UNDEFINED
|
|
||||||
else
|
|
||||||
throw FatalAstException("unknown dt $srcCall")
|
throw FatalAstException("unknown dt $srcCall")
|
||||||
}
|
}
|
||||||
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
||||||
@ -237,28 +223,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transform(gosub: GoSub): PtFunctionCall {
|
|
||||||
// Gather the Goto and any preceding parameter assignments back into a single Function call node.
|
|
||||||
// (the reason it was split up in the first place, is because the Compiler Ast optimizers
|
|
||||||
// can then work on any complex expressions that are used as arguments.)
|
|
||||||
val arguments = determineGosubArguments(gosub)
|
|
||||||
|
|
||||||
val parameters = gosub.identifier.targetSubroutine(program)!!.parameters
|
|
||||||
if(arguments.size != parameters.size)
|
|
||||||
throw FatalAstException("mismatched number of parameter assignments for function call")
|
|
||||||
|
|
||||||
val target = transform(gosub.identifier)
|
|
||||||
val call = PtFunctionCall(target.targetName, true, DataType.UNDEFINED, gosub.position)
|
|
||||||
|
|
||||||
// put arguments in correct order for the parameters
|
|
||||||
parameters.forEach {
|
|
||||||
val argument = arguments.getValue(it.name)
|
|
||||||
call.add(transformExpression(argument))
|
|
||||||
}
|
|
||||||
|
|
||||||
return call
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transform(srcIf: IfElse): PtIfElse {
|
private fun transform(srcIf: IfElse): PtIfElse {
|
||||||
val ifelse = PtIfElse(srcIf.position)
|
val ifelse = PtIfElse(srcIf.position)
|
||||||
ifelse.add(transformExpression(srcIf.condition))
|
ifelse.add(transformExpression(srcIf.condition))
|
||||||
@ -287,14 +251,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(label: Label): PtLabel =
|
private fun transform(label: Label): PtLabel =
|
||||||
PtLabel(label.name, label.position)
|
PtLabel(label.name, label.position)
|
||||||
|
|
||||||
private fun transform(srcPipe: Pipe): PtPipe {
|
|
||||||
val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position)
|
|
||||||
pipe.add(transformExpression(srcPipe.source))
|
|
||||||
for (segment in srcPipe.segments)
|
|
||||||
pipe.add(transformExpression(segment))
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
|
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
|
||||||
val post = PtPostIncrDecr(src.operator, src.position)
|
val post = PtPostIncrDecr(src.operator, src.position)
|
||||||
post.add(transform(src.target))
|
post.add(transform(src.target))
|
||||||
@ -329,6 +285,7 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
srcSub.asmAddress,
|
srcSub.asmAddress,
|
||||||
srcSub.asmClobbers,
|
srcSub.asmClobbers,
|
||||||
params,
|
params,
|
||||||
|
srcSub.returntypes,
|
||||||
srcSub.asmReturnvaluesRegisters,
|
srcSub.asmReturnvaluesRegisters,
|
||||||
srcSub.inline,
|
srcSub.inline,
|
||||||
srcSub.position)
|
srcSub.position)
|
||||||
@ -419,7 +376,8 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
|
|
||||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||||
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
val actualType = if(type==DataType.BOOL) DataType.UBYTE else type
|
||||||
|
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)
|
||||||
expr.add(transformExpression(srcExpr.left))
|
expr.add(transformExpression(srcExpr.left))
|
||||||
expr.add(transformExpression(srcExpr.right))
|
expr.add(transformExpression(srcExpr.right))
|
||||||
return expr
|
return expr
|
||||||
@ -459,15 +417,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(number: NumericLiteral): PtNumber =
|
private fun transform(number: NumericLiteral): PtNumber =
|
||||||
PtNumber(number.type, number.number, number.position)
|
PtNumber(number.type, number.number, number.position)
|
||||||
|
|
||||||
private fun transform(srcPipe: PipeExpression): PtPipe {
|
|
||||||
val type = srcPipe.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
|
||||||
val pipe = PtPipe(type, false, srcPipe.position)
|
|
||||||
pipe.add(transformExpression(srcPipe.source))
|
|
||||||
for (segment in srcPipe.segments)
|
|
||||||
pipe.add(transformExpression(segment))
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
||||||
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||||
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
|
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
|
||||||
|
@ -9,6 +9,7 @@ import prog8.ast.walk.IAstVisitor
|
|||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.BuiltinFunctions
|
import prog8.compiler.BuiltinFunctions
|
||||||
|
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -77,16 +78,30 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!valueDt.isKnown) {
|
if(!valueDt.isKnown) {
|
||||||
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
|
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED))
|
if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED)) {
|
||||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}", returnStmt.value!!.position)
|
if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) {
|
||||||
|
// if the return value is a bool and the return type is ubyte, allow this. But give a warning.
|
||||||
|
errors.warn("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position)
|
||||||
|
} else if(valueDt istype DataType.UBYTE && expectedReturnValues[0] == DataType.BOOL) {
|
||||||
|
// if the return value is ubyte and the return type is bool, allow this only if value is 0 or 1
|
||||||
|
val returnValue = returnStmt.value as? NumericLiteral
|
||||||
|
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
|
||||||
|
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(returnStmt)
|
super.visit(returnStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(ifElse: IfElse) {
|
override fun visit(ifElse: IfElse) {
|
||||||
if(!ifElse.condition.inferType(program).isInteger)
|
val dt = ifElse.condition.inferType(program)
|
||||||
errors.err("condition value should be an integer type", ifElse.condition.position)
|
if(!dt.isInteger && !dt.istype(DataType.BOOL))
|
||||||
|
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
|
||||||
super.visit(ifElse)
|
super.visit(ifElse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +133,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||||
}
|
}
|
||||||
|
DataType.BOOL -> {
|
||||||
|
if(iterableDt != DataType.ARRAY_BOOL)
|
||||||
|
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
|
||||||
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
||||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||||
@ -183,16 +202,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(gosub: GoSub) {
|
|
||||||
val targetStatement = checkFunctionOrLabelExists(gosub.identifier, gosub)
|
|
||||||
if(targetStatement!=null) {
|
|
||||||
if(targetStatement is BuiltinFunctionPlaceholder)
|
|
||||||
errors.err("can't gosub to a builtin function", gosub.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(gosub)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
override fun visit(block: Block) {
|
||||||
val addr = block.address
|
val addr = block.address
|
||||||
if(addr!=null && addr>65535u) {
|
if(addr!=null && addr>65535u) {
|
||||||
@ -294,15 +303,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
if (subroutine.returntypes.isNotEmpty()) {
|
if (subroutine.returntypes.isNotEmpty()) {
|
||||||
// for asm subroutines with an address, no statement check is possible.
|
// for asm subroutines with an address, no statement check is possible.
|
||||||
if (subroutine.asmAddress == null && !subroutine.inline)
|
if (subroutine.asmAddress == null && !subroutine.inline)
|
||||||
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)")
|
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or the assembler equivalent in case of %asm)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
|
||||||
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
|
||||||
if(!subroutine.isAsmSubroutine && compilerOptions.compTarget.name!=VMTarget.NAME)
|
|
||||||
subroutine.inline = false
|
|
||||||
|
|
||||||
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
|
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
|
||||||
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||||
|
|
||||||
@ -313,8 +317,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("number of return registers is not the isSameAs as number of return values")
|
err("number of return registers is not the isSameAs as number of return values")
|
||||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||||
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||||
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE)
|
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
|
||||||
err("parameter '${param.first.name}' should be (u)byte")
|
err("parameter '${param.first.name}' should be (u)byte or bool")
|
||||||
}
|
}
|
||||||
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||||
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
||||||
@ -328,17 +332,17 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
|
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
|
||||||
if(pair.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
if(pair.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||||
if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE)
|
if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE && pair.first != DataType.BOOL)
|
||||||
err("return value #${index + 1} should be (u)byte")
|
err("return type #${index + 1} should be (u)byte")
|
||||||
}
|
}
|
||||||
else if(pair.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||||
if (pair.first != DataType.UWORD && pair.first != DataType.WORD
|
if (pair.first != DataType.UWORD && pair.first != DataType.WORD
|
||||||
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT)
|
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT)
|
||||||
err("return value #${index + 1} should be (u)word/address")
|
err("return type #${index + 1} should be (u)word/address")
|
||||||
}
|
}
|
||||||
else if(pair.second.statusflag!=null) {
|
else if(pair.second.statusflag!=null) {
|
||||||
if (pair.first != DataType.UBYTE)
|
if (pair.first != DataType.UBYTE)
|
||||||
err("return value #${index + 1} should be ubyte")
|
err("return type #${index + 1} should be ubyte")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,14 +426,16 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(untilLoop: UntilLoop) {
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
if(!untilLoop.condition.inferType(program).isInteger)
|
val dt = untilLoop.condition.inferType(program)
|
||||||
errors.err("condition value should be an integer type", untilLoop.condition.position)
|
if(!dt.isInteger && !dt.istype(DataType.BOOL))
|
||||||
|
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
|
||||||
super.visit(untilLoop)
|
super.visit(untilLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop) {
|
override fun visit(whileLoop: WhileLoop) {
|
||||||
if(!whileLoop.condition.inferType(program).isInteger)
|
val dt = whileLoop.condition.inferType(program)
|
||||||
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
if(!dt.isInteger && !dt.istype(DataType.BOOL))
|
||||||
|
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,6 +472,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(numvalue!=null && targetDt.isKnown)
|
if(numvalue!=null && targetDt.isKnown)
|
||||||
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
|
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
|
||||||
|
|
||||||
|
// for now, don't enforce bool type with only logical operators...
|
||||||
|
// if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
|
||||||
|
// val operator = (assignment.value as? BinaryExpression)?.operator
|
||||||
|
// if(operator in InvalidOperatorsForBoolean)
|
||||||
|
// errors.err("can't use boolean operand with this operator $operator", assignment.position)
|
||||||
|
// }
|
||||||
|
|
||||||
super.visit(assignment)
|
super.visit(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +520,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(constVal==null) {
|
if(constVal==null) {
|
||||||
val sourceDatatype = assignment.value.inferType(program)
|
val sourceDatatype = assignment.value.inferType(program)
|
||||||
if (sourceDatatype.isUnknown) {
|
if (sourceDatatype.isUnknown) {
|
||||||
if (assignment.value !is FunctionCallExpression && assignment.value !is PipeExpression)
|
if (assignment.value !is FunctionCallExpression)
|
||||||
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
|
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
|
||||||
} else {
|
} else {
|
||||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||||
@ -776,8 +789,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||||
if(e is IdentifierReference) {
|
if(e is IdentifierReference) {
|
||||||
val decl = e.targetVarDecl(program)!!
|
val decl = e.targetVarDecl(program)
|
||||||
return decl.datatype in PassByReferenceDatatypes
|
return if(decl!=null)
|
||||||
|
decl.datatype in PassByReferenceDatatypes
|
||||||
|
else
|
||||||
|
true // is probably a symbol that needs addr-of
|
||||||
}
|
}
|
||||||
return e is StringLiteral
|
return e is StringLiteral
|
||||||
}
|
}
|
||||||
@ -827,10 +843,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("can only take negative of a signed number type", expr.position)
|
errors.err("can only take negative of a signed number type", expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator == "not") {
|
|
||||||
if(dt !in IntegerDatatypes)
|
|
||||||
errors.err("can only use boolean not on integer types", expr.position)
|
|
||||||
}
|
|
||||||
else if(expr.operator == "~") {
|
else if(expr.operator == "~") {
|
||||||
if(dt !in IntegerDatatypes)
|
if(dt !in IntegerDatatypes)
|
||||||
errors.err("can only use bitwise invert on integer types", expr.position)
|
errors.err("can only use bitwise invert on integer types", expr.position)
|
||||||
@ -867,15 +879,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
|
errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"and", "or", "xor" -> {
|
|
||||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
|
||||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
|
||||||
errors.err("logical operator can only be used on boolean operands", expr.right.position)
|
|
||||||
val constLeft = expr.left.constValue(program)
|
|
||||||
val constRight = expr.right.constValue(program)
|
|
||||||
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
|
|
||||||
errors.err("const literal argument of logical operator must be boolean (0 or 1)", expr.position)
|
|
||||||
}
|
|
||||||
"&", "|", "^" -> {
|
"&", "|", "^" -> {
|
||||||
// only integer numeric operands accepted
|
// only integer numeric operands accepted
|
||||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||||
@ -884,15 +887,17 @@ internal class AstChecker(private val program: Program,
|
|||||||
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
|
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftDt !in NumericDatatypes && leftDt != DataType.STR)
|
if(leftDt !in NumericDatatypes && leftDt != DataType.STR && leftDt != DataType.BOOL)
|
||||||
errors.err("left operand is not numeric or str", expr.left.position)
|
errors.err("left operand is not numeric or str", expr.left.position)
|
||||||
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
|
if(rightDt!in NumericDatatypes && rightDt != DataType.STR && rightDt != DataType.BOOL)
|
||||||
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
|
// only 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) {
|
||||||
|
// expression with one side BOOL other side (U)BYTE is allowed; bool==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)
|
||||||
}
|
}
|
||||||
@ -901,10 +906,31 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(expr.operator !in ComparisonOperators) {
|
if(expr.operator !in ComparisonOperators) {
|
||||||
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 expressions with strings or arrays are possible", expr.position)
|
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(leftDt==DataType.BOOL || rightDt==DataType.BOOL ||
|
||||||
|
(expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true ||
|
||||||
|
(expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true) {
|
||||||
|
if(expr.operator in setOf("<", "<=", ">", ">=")) {
|
||||||
|
errors.err("can't use boolean operand with this comparison operator", expr.position)
|
||||||
|
}
|
||||||
|
// for now, don't enforce bool type with only logical operators...
|
||||||
|
// if(expr.operator in InvalidOperatorsForBoolean && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||||
|
// errors.err("can't use boolean operand with this operator ${expr.operator}", expr.left.position)
|
||||||
|
// }
|
||||||
|
if(expr.operator == "==" || expr.operator == "!=") {
|
||||||
|
val leftNum = expr.left.constValue(program)?.number ?: 0.0
|
||||||
|
val rightNum = expr.right.constValue(program)?.number ?: 0.0
|
||||||
|
if(leftNum>1.0 || rightNum>1.0 || leftNum<0.0 || rightNum<0.0) {
|
||||||
|
errors.warn("expression is always false", expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((expr.operator == "/" || expr.operator == "%") && ( rightDt==DataType.BOOL || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||||
|
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression) {
|
override fun visit(typecast: TypecastExpression) {
|
||||||
@ -1013,7 +1039,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||||
if(targetStatement!=null) {
|
if(targetStatement!=null) {
|
||||||
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||||
checkUnusedReturnValues(functionCallStatement, targetStatement, program, errors)
|
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
val funcName = functionCallStatement.target.nameInSource
|
val funcName = functionCallStatement.target.nameInSource
|
||||||
@ -1041,7 +1067,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(funcName[0] in arrayOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
||||||
// in-place modification, can't be done on literals
|
// in-place modification, can't be done on literals
|
||||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||||
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||||
@ -1062,20 +1088,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("cannot use arguments when calling a label", position)
|
errors.err("cannot use arguments when calling a label", position)
|
||||||
|
|
||||||
if(target is BuiltinFunctionPlaceholder) {
|
if(target is BuiltinFunctionPlaceholder) {
|
||||||
if(target.name=="swap") {
|
if(target.name=="all" || target.name=="any") {
|
||||||
// swap() is a bit weird because this one is translated into an operations directly, instead of being a function call
|
|
||||||
val dt1 = args[0].inferType(program)
|
|
||||||
val dt2 = args[1].inferType(program)
|
|
||||||
if (dt1 != dt2)
|
|
||||||
errors.err("swap requires 2 args of identical type", position)
|
|
||||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
|
||||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
|
||||||
else if(args[0] isSameAs args[1])
|
|
||||||
errors.err("swap should have 2 different args", position)
|
|
||||||
else if(!dt1.isNumeric)
|
|
||||||
errors.err("swap requires args of numerical type", position)
|
|
||||||
}
|
|
||||||
else if(target.name=="all" || target.name=="any") {
|
|
||||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|
||||||
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
|
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
|
||||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||||
@ -1102,8 +1115,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
||||||
var regname = ident.nameInSource[1].uppercase()
|
var regname = ident.nameInSource[1].uppercase()
|
||||||
if(regname.endsWith('L') || regname.endsWith('H') || regname.endsWith('s'))
|
val lastLetter = regname.last().lowercaseChar()
|
||||||
regname=regname.substring(0, regname.length-1)
|
if(lastLetter in setOf('l', 'h', 's')) {
|
||||||
|
regname = regname.substring(0, regname.length - 1)
|
||||||
|
val lastLetter2 = regname.last().lowercaseChar()
|
||||||
|
if(lastLetter2 in setOf('l', 'h', 's')) {
|
||||||
|
regname = regname.substring(0, regname.length - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
val reg = RegisterOrPair.valueOf(regname)
|
val reg = RegisterOrPair.valueOf(regname)
|
||||||
val same = params.filter { it.value.registerOrPair==reg }
|
val same = params.filter { it.value.registerOrPair==reg }
|
||||||
for(s in same) {
|
for(s in same) {
|
||||||
@ -1117,33 +1136,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(pipe: PipeExpression) {
|
|
||||||
process(pipe)
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(pipe: Pipe) {
|
|
||||||
process(pipe)
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun process(pipe: IPipe) {
|
|
||||||
if(pipe.source in pipe.segments)
|
|
||||||
throw InternalCompilerException("pipe source and segments should all be different nodes")
|
|
||||||
if (pipe.segments.isEmpty())
|
|
||||||
throw FatalAstException("pipe is missing one or more expressions")
|
|
||||||
if(pipe.segments.any { it !is IFunctionCall })
|
|
||||||
throw FatalAstException("pipe segments can only be function calls")
|
|
||||||
|
|
||||||
if(compilerOptions.compTarget !is VMTarget) {
|
|
||||||
pipe.segments.forEach {
|
|
||||||
it as IFunctionCall
|
|
||||||
if (it.args.size > 0)
|
|
||||||
errors.err("only unary functions supported in pipe expressions for now", it.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||||
if(postIncrDecr.target.identifier != null) {
|
if(postIncrDecr.target.identifier != null) {
|
||||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||||
@ -1170,14 +1162,19 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
|
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
|
||||||
|
|
||||||
|
if(postIncrDecr.target.inferType(program) istype DataType.BOOL) {
|
||||||
|
errors.err("can't use boolean operand with this operator ${postIncrDecr.operator}", postIncrDecr.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(postIncrDecr)
|
super.visit(postIncrDecr)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
|
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
|
||||||
if(target is VarDecl) {
|
if(target is VarDecl) {
|
||||||
if(target.datatype !in IterableDatatypes)
|
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
|
||||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
||||||
val arraysize = target.arraysize?.constIndex()
|
val arraysize = target.arraysize?.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
// check out of bounds
|
// check out of bounds
|
||||||
@ -1427,6 +1424,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
if (number < -32768 || number > 32767)
|
if (number < -32768 || number > 32767)
|
||||||
return err("value '$number' out of range for word")
|
return err("value '$number' out of range for word")
|
||||||
}
|
}
|
||||||
|
DataType.BOOL -> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
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
|
||||||
@ -1486,12 +1486,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val result = when(targetDatatype) {
|
val result = when(targetDatatype) {
|
||||||
DataType.BYTE -> sourceDatatype== DataType.BYTE
|
DataType.BOOL -> sourceDatatype in NumericDatatypes
|
||||||
DataType.UBYTE -> sourceDatatype== DataType.UBYTE
|
DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL
|
||||||
DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD
|
DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL
|
||||||
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL)
|
||||||
|
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL
|
||||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype == DataType.STR
|
||||||
else -> {
|
else -> {
|
||||||
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||||
false
|
false
|
||||||
@ -1515,7 +1516,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, program: Program, errors: IErrorReporter) {
|
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||||
if (!call.void) {
|
if (!call.void) {
|
||||||
// check for unused return values
|
// check for unused return values
|
||||||
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user