mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 01:23:34 +00:00
Compare commits
531 Commits
Author | SHA1 | Date | |
---|---|---|---|
914f19be86 | |||
d0b18dec8e | |||
549c598f51 | |||
7b59bc8d12 | |||
79d0fb0b52 | |||
edf56d34f8 | |||
6733253826 | |||
86210c4513 | |||
e08da659e5 | |||
e9ec310d8a | |||
19a2791c65 | |||
f1a7d5ecf7 | |||
8b05abb80d | |||
9a2df072cc | |||
224278e07a | |||
74b69e191e | |||
8cda8a727c | |||
4403e4ed62 | |||
6ee270d9d8 | |||
44fa309d20 | |||
58d88f3dd4 | |||
e980c23177 | |||
75224321bb | |||
801af05b20 | |||
7611dbbddc | |||
6d40ca15bc | |||
32c1c19224 | |||
bbf6357222 | |||
dc16629c24 | |||
3718b9d768 | |||
c25eb088ec | |||
3feb3e52f8 | |||
8e730ef93d | |||
e0913a39ab | |||
7a27fbc001 | |||
ee0dbdad35 | |||
9225f88f89 | |||
a04839dd6b | |||
002006517a | |||
f5b202d438 | |||
a7df094ff4 | |||
1e6fa77633 | |||
eb4cff202c | |||
7ee777f405 | |||
81bd5c784e | |||
b526e132a7 | |||
1860f66de5 | |||
ded9ada9bc | |||
d0e6a2eb8b | |||
4e103a1963 | |||
475e927178 | |||
ca7932c4f0 | |||
8ab47d3321 | |||
def7e87151 | |||
27568c2bef | |||
0694a187d7 | |||
832601b36b | |||
578969c34c | |||
d1d0115aed | |||
c89e6ebfab | |||
ca1089b881 | |||
a1d04f2aad | |||
bf0604133c | |||
a82b2da16e | |||
f2273c0acc | |||
17bedac96c | |||
4831fad27a | |||
5e896cf582 | |||
add3491c57 | |||
f470576822 | |||
10760a53a8 | |||
eee805183c | |||
b8fb391022 | |||
3c698f1584 | |||
2fad52d684 | |||
ec64a68a71 | |||
db55562f6a | |||
d8409a9d2b | |||
0d0ce6eec1 | |||
483f313eda | |||
7b6c742178 | |||
d4a35ba6ff | |||
68b112837a | |||
e2f20ebf94 | |||
f870e4965a | |||
7ebcb219d6 | |||
c21913a66b | |||
77e956a29f | |||
08275c406a | |||
2931e1b87b | |||
153b422496 | |||
0f6a6d6fea | |||
91fdb3e2d4 | |||
d8e87bd881 | |||
922033c1b2 | |||
df1793efbf | |||
836a2700f2 | |||
8f3aaf77a1 | |||
00c059e5b1 | |||
f4f355c74a | |||
b465fc5aaf | |||
2d78eaa48d | |||
d08451bccc | |||
d8e785aed0 | |||
267b6f49b5 | |||
e6688f4b9d | |||
9d7b9771c2 | |||
136a9a39de | |||
3dcf628fdb | |||
e614e9787a | |||
e426fc0922 | |||
5d4bfffc7e | |||
207cdaf7a4 | |||
7315b581ce | |||
38efaae7b2 | |||
469e042216 | |||
0f1a4b9d8f | |||
7303c00296 | |||
fc55b34d84 | |||
6f67fc0e02 | |||
562d722ad5 | |||
144c1ba3a6 | |||
06b032af91 | |||
3603140114 | |||
e094785cbd | |||
e7408224ac | |||
e67c05c274 | |||
b22804efaf | |||
890f55f91a | |||
cc5fc0b892 | |||
5efe2b027a | |||
5b6569d0f9 | |||
0eda7ac498 | |||
a5ef353484 | |||
67a36d8d31 | |||
7cc3cc3990 | |||
dc0edc4c2b | |||
71d2f091e5 | |||
c2f062a391 | |||
224f490455 | |||
5b35232ab4 | |||
6d6db70e42 | |||
6830e15b4e | |||
3f07cad35d | |||
e951340033 | |||
db8912a735 | |||
0e297731a3 | |||
f20c4f98ac | |||
05e60cc7c0 | |||
55b4469767 | |||
f15516e478 | |||
17ceadbadf | |||
8c25b2b316 | |||
8b1ae404a3 | |||
13534cd4a9 | |||
abfb345503 | |||
42ae935496 | |||
434515d957 | |||
094f7803b7 | |||
b0c7bad391 | |||
e9a4a905ef | |||
7b6cd0cfbe | |||
b718b12083 | |||
cfa7258ff4 | |||
b70e0a0870 | |||
da8eb464b8 | |||
8f9d1cfa30 | |||
585009ac5c | |||
30ee65fd14 | |||
76428b16f0 | |||
0d7b14e2d8 | |||
a9d19d02b3 | |||
adcbe55307 | |||
aa99a7df64 | |||
00afa1ce52 | |||
e94bf4c63c | |||
ec5adffdc2 | |||
733c17ad3a | |||
53b0b562e6 | |||
fabae6e970 | |||
a9f9c40d8a | |||
6fc89607d3 | |||
2340760f53 | |||
39d6d2857e | |||
7b722a0001 | |||
e7682119e0 | |||
af6be44676 | |||
5a8f97a0b6 | |||
0d4dd385b8 | |||
94f0f3e966 | |||
43e31765e5 | |||
7c1bdfe713 | |||
9f09784b55 | |||
e7a3a89bfb | |||
7ea7e63f44 | |||
1d2ce2cbeb | |||
06cf2e0bd7 | |||
9d219ae4b9 | |||
71f5a6c50e | |||
90b2be2bf4 | |||
db1aa8fcbd | |||
11c000f764 | |||
4d6dcbd173 | |||
0da117efd2 | |||
533c368e32 | |||
8883513b0e | |||
dcc9a71455 | |||
1a56743bb1 | |||
387a4b7c35 | |||
1d65d63bd9 | |||
dda19c29fe | |||
ca41669f4f | |||
0e1886e6bd | |||
c26e116f0e | |||
5c9c7f2c5e | |||
ca2fb6cef3 | |||
46dac909ef | |||
b1e4347e10 | |||
97aa91c75e | |||
4f8fb32136 | |||
e0fbce0087 | |||
fb22f78fb3 | |||
d6393cdbe5 | |||
5167fdb3f0 | |||
ab00822764 | |||
b4352ad38b | |||
d07d00fa41 | |||
11d87e4725 | |||
627ed51a1b | |||
c8f3bfa726 | |||
3091e3a1c8 | |||
2f3e7d1c27 | |||
0e831d4b92 | |||
7294ec9a3c | |||
e34bab9585 | |||
7dd14955c1 | |||
6428ced157 | |||
30a42ec1bd | |||
aacea3e9db | |||
6886b61186 | |||
0744c9fa29 | |||
502a665ffc | |||
3c315703c0 | |||
12ed07a607 | |||
101b33c381 | |||
97f4316653 | |||
b0704e86f0 | |||
a182b13e5a | |||
80b630a1e4 | |||
475efbe007 | |||
3ab5e5ac48 | |||
c6c5ff2089 | |||
176ec8ac7d | |||
dcdd4b3255 | |||
fc0a0105b3 | |||
f3960d21a8 | |||
a44d853c1b | |||
6b41734d6a | |||
c33dc0f3be | |||
bb5ffb24a8 | |||
a878c9a61d | |||
6454bf8ec4 | |||
40aa733ea7 | |||
f37a822725 | |||
f249ccd414 | |||
7ef4ddf0f3 | |||
d8e18df3a1 | |||
78d3d9d27d | |||
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 | |||
6ed5f04970 | |||
b459b09b2f | |||
3f5877dbcc | |||
e659b91c4d | |||
e09f054058 | |||
b646f50265 | |||
0a48ef3030 | |||
ba614801ee | |||
fd6eb47e68 | |||
e69aeb8b98 | |||
26ea1da146 | |||
c9e8c7a290 | |||
5e4eb92443 | |||
461b6499ef | |||
c769920b6e | |||
181b98ef9e | |||
4e1184a400 | |||
e52d9e3210 | |||
dc6475c91b | |||
52f9956e92 | |||
0bf00d1ca4 | |||
d1a707df57 | |||
4dc9b45297 | |||
6e31eebfb5 | |||
a7df828932 | |||
517cf61d11 | |||
4be7bc8323 | |||
74c05d00a9 | |||
677613d30a | |||
bacba629a5 | |||
14e36f1362 | |||
d43ad849d1 | |||
627aa61184 | |||
dad5b17ac8 | |||
fef52c0112 | |||
8c4765b386 | |||
7c121bfc01 | |||
942c5cc04b | |||
348b3036ff | |||
09d3451d9d | |||
b1a49e5f29 | |||
da01a5b4dc | |||
3f9cdd9b56 | |||
0f9e87d7bb | |||
0869789214 | |||
10c8cc35c5 | |||
30c2e3e8ff | |||
86cc2f1075 | |||
fa357a450b | |||
b32641db87 | |||
0ee790969d | |||
7844ace934 | |||
f4993d6e5d | |||
0fab806f36 | |||
be2113d291 | |||
625d5b2313 | |||
6471c0c536 | |||
47c53fa60a | |||
cf50e4f6ec | |||
7eea97d741 | |||
88b55ab93e | |||
ee36d47c27 | |||
6f2fdbe447 | |||
0f36be0001 | |||
0f4a197e34 | |||
7dbff5b9e6 | |||
220246278a | |||
349e5a15e9 | |||
bf7f4bba7b | |||
ab1766a559 | |||
51bf33040a | |||
a2c7273801 | |||
ec6ac5bf24 | |||
ec7501782d | |||
890b1c2d52 | |||
c25d07259a | |||
c960246eee | |||
a01aee3111 | |||
e2e951efdf | |||
3f6393f732 | |||
b6eb343234 | |||
207a7e5160 | |||
a0face4a28 | |||
a8cf9f5cc4 | |||
461b38e653 | |||
8e4c0f7c22 | |||
d78bfcc35c | |||
2b7c09e6ee | |||
036d9dbe59 | |||
1d342cc6af | |||
62b32b2211 | |||
ae45ce517e | |||
5b3ccab7dc | |||
95f16c38a9 | |||
d616cb283b | |||
9874fe2c23 |
31
.github/workflows/all-ci.yml
vendored
Normal file
31
.github/workflows/all-ci.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
name: Build and Test the Prog8 compiler
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install 64tass
|
||||||
|
run: sudo apt-get update -y && sudo apt-get install -y 64tass
|
||||||
|
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: adopt
|
||||||
|
|
||||||
|
- name: Build and test with Gradle
|
||||||
|
run: ./gradlew build shadowJar --no-daemon
|
||||||
|
|
||||||
|
- name: Create compiler shadowJar artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: prog8-compiler-jar-zipped
|
||||||
|
path: compiler/build/libs/*-all.jar
|
12
.idea/libraries/antlr_antlr4.xml
generated
12
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,17 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="antlr.antlr4" type="repository">
|
<library name="antlr.antlr4" type="repository">
|
||||||
<properties maven-id="org.antlr:antlr4:4.9.3">
|
<properties maven-id="org.antlr:antlr4:4.11.1">
|
||||||
<exclude>
|
<exclude>
|
||||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||||
</exclude>
|
</exclude>
|
||||||
</properties>
|
</properties>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.9.3/antlr4-4.9.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.9.3/antlr4-runtime-4.9.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.11.1/antlr4-runtime-4.11.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.2/antlr-runtime-3.5.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.1/ST4-4.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
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 />
|
||||||
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -19,7 +19,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<type id="Python" />
|
<type id="Python" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -2,10 +2,10 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeAst/codeAst.iml" filepath="$PROJECT_DIR$/codeAst/codeAst.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" filepath="$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||||
@ -14,6 +14,7 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
|
7
LICENSE
7
LICENSE
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
This sofware license is for Prog8 the compiler + associated libraries.
|
This sofware license is for Prog8 the compiler + associated library files.
|
||||||
The software generated by running the compiler is excluded from this.
|
|
||||||
|
Exception: All output files generated by the compiler (intermediary files
|
||||||
|
and compiled binary programs) are excluded from this; you can do with those
|
||||||
|
whatever you want.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -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?
|
||||||
@ -77,6 +78,9 @@ It's handy to have an emulator (or a real machine perhaps!) to run the programs
|
|||||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
||||||
|
|
||||||
|
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||||
|
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
|
||||||
|
|
||||||
|
|
||||||
Example code
|
Example code
|
||||||
------------
|
------------
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
package prog8.code.ast
|
|
||||||
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.Encoding
|
|
||||||
import prog8.code.core.Position
|
|
||||||
|
|
||||||
|
|
||||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
|
||||||
override fun printProperties() {
|
|
||||||
print(type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
|
||||||
val identifier: PtIdentifier
|
|
||||||
get() = children.single() as PtIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) {
|
|
||||||
val variable: PtIdentifier
|
|
||||||
get() = children[0] as PtIdentifier
|
|
||||||
val index: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtArray(type: DataType, position: Position): PtExpression(type, position)
|
|
||||||
|
|
||||||
|
|
||||||
class PtBuiltinFunctionCall(val name: String, val void: Boolean, type: DataType, position: Position) : PtExpression(type, position) {
|
|
||||||
init {
|
|
||||||
if(!void)
|
|
||||||
require(type!=DataType.UNDEFINED)
|
|
||||||
}
|
|
||||||
|
|
||||||
val args: List<PtExpression>
|
|
||||||
get() = children.map { it as PtExpression }
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$name void=$void")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
|
||||||
val left: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val right: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$operator -> $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
|
||||||
val element: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val iterable: PtIdentifier
|
|
||||||
get() = children[1] as PtIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtFunctionCall(val functionName: List<String>,
|
|
||||||
val void: Boolean,
|
|
||||||
type: DataType,
|
|
||||||
position: Position) : PtExpression(type, position) {
|
|
||||||
init {
|
|
||||||
if(!void)
|
|
||||||
require(type!=DataType.UNDEFINED)
|
|
||||||
}
|
|
||||||
|
|
||||||
val args: List<PtExpression>
|
|
||||||
get() = children.map { it as PtExpression }
|
|
||||||
override fun printProperties() {
|
|
||||||
print("${functionName.joinToString(".")} void=$void")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtIdentifier(val ref: List<String>, val targetName: List<String>, type: DataType, position: Position) : PtExpression(type, position) {
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$ref --> $targetName $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
|
||||||
val address: PtExpression
|
|
||||||
get() = children.single() as PtExpression
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$number ($type)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
|
|
||||||
init {
|
|
||||||
if(!void)
|
|
||||||
require(type!=DataType.UNDEFINED)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
|
||||||
val value: PtExpression
|
|
||||||
get() = children.single() as PtExpression
|
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print(operator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtRange(type: DataType, position: Position) : PtExpression(type, position) {
|
|
||||||
val from: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val to: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
val step: PtNumber
|
|
||||||
get() = children[2] as PtNumber
|
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$encoding:\"$value\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
|
|
||||||
val value: PtExpression
|
|
||||||
get() = children.single() as PtExpression
|
|
||||||
}
|
|
@ -24,9 +24,9 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':virtualmachine')
|
// 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 {
|
||||||
|
@ -10,6 +10,5 @@
|
|||||||
<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="module" module-name="virtualmachine" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,8 +1,7 @@
|
|||||||
package prog8
|
package prog8.code
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By convention, the right side of an `Either` is used to hold successful values.
|
* By convention, the right side of an `Either` is used to hold successful values.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
sealed class Either<out L, out R> {
|
sealed class Either<out L, out R> {
|
||||||
|
|
@ -5,19 +5,16 @@ import prog8.code.core.*
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tree structure containing all symbol definitions in the program
|
* Tree structure containing all symbol definitions in the program
|
||||||
* (blocks, subroutines, variables (all types) and labels).
|
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||||
*/
|
*/
|
||||||
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
||||||
fun print() = printIndented(0)
|
|
||||||
|
|
||||||
override fun printProperties() { }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The table as a flat mapping of scoped names to the StNode.
|
* The table as a flat mapping of scoped names to the StNode.
|
||||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||||
*/
|
*/
|
||||||
val flat: Map<List<String>, StNode> by lazy {
|
|
||||||
val result = mutableMapOf<List<String>, StNode>()
|
val flat: Map<String, StNode> by lazy {
|
||||||
|
val result = mutableMapOf<String, StNode>()
|
||||||
fun flatten(node: StNode) {
|
fun flatten(node: StNode) {
|
||||||
result[node.scopedName] = node
|
result[node.scopedName] = node
|
||||||
node.children.values.forEach { flatten(it) }
|
node.children.values.forEach { flatten(it) }
|
||||||
@ -40,7 +37,25 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
|||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>) = flat[scopedName]
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||||
|
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +69,8 @@ enum class StNodeType {
|
|||||||
STATICVAR,
|
STATICVAR,
|
||||||
MEMVAR,
|
MEMVAR,
|
||||||
CONSTANT,
|
CONSTANT,
|
||||||
BUILTINFUNC
|
BUILTINFUNC,
|
||||||
|
MEMORYSLAB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -66,38 +82,18 @@ open class StNode(val name: String,
|
|||||||
|
|
||||||
lateinit var parent: StNode
|
lateinit var parent: StNode
|
||||||
|
|
||||||
val scopedName: List<String> by lazy {
|
val scopedName: String by lazy { scopedNameList.joinToString(".") }
|
||||||
if(type== StNodeType.GLOBAL)
|
|
||||||
emptyList()
|
|
||||||
else
|
|
||||||
parent.scopedName + name
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lookup(name: String) =
|
open fun lookup(scopedName: String) =
|
||||||
lookupUnqualified(name)
|
lookup(scopedName.split('.'))
|
||||||
open fun lookup(scopedName: List<String>) =
|
|
||||||
if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0])
|
|
||||||
fun lookupOrElse(name: String, default: () -> StNode) =
|
|
||||||
lookupUnqualified(name) ?: default()
|
|
||||||
fun lookupOrElse(scopedName: List<String>, default: () -> StNode) =
|
|
||||||
lookup(scopedName) ?: default()
|
|
||||||
|
|
||||||
private fun lookupQualified(scopedName: List<String>): StNode? {
|
fun lookupUnscopedOrElse(name: String, default: () -> StNode) =
|
||||||
// a scoped name refers to a name in another namespace, and always stars from the root.
|
lookupUnscoped(name) ?: default()
|
||||||
var node = this
|
|
||||||
while(node.type!= StNodeType.GLOBAL)
|
|
||||||
node = node.parent
|
|
||||||
|
|
||||||
for(name in scopedName) {
|
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
|
||||||
if(name in node.children)
|
lookup(scopedName.split('.')) ?: default()
|
||||||
node = node.children.getValue(name)
|
|
||||||
else
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun lookupUnqualified(name: String): StNode? {
|
fun lookupUnscoped(name: String): StNode? {
|
||||||
// first consider the builtin functions
|
// first consider the builtin functions
|
||||||
var globalscope = this
|
var globalscope = this
|
||||||
while(globalscope.type!= StNodeType.GLOBAL)
|
while(globalscope.type!= StNodeType.GLOBAL)
|
||||||
@ -119,99 +115,110 @@ open class StNode(val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printIndented(indent: Int) {
|
|
||||||
print(" ".repeat(indent))
|
|
||||||
when(type) {
|
|
||||||
StNodeType.GLOBAL -> print("SYMBOL-TABLE:")
|
|
||||||
StNodeType.BLOCK -> print("(B) ")
|
|
||||||
StNodeType.SUBROUTINE -> print("(S) ")
|
|
||||||
StNodeType.LABEL -> print("(L) ")
|
|
||||||
StNodeType.STATICVAR -> print("(V) ")
|
|
||||||
StNodeType.MEMVAR -> print("(M) ")
|
|
||||||
StNodeType.CONSTANT -> print("(C) ")
|
|
||||||
StNodeType.BUILTINFUNC -> print("(F) ")
|
|
||||||
StNodeType.ROMSUB -> print("(R) ")
|
|
||||||
}
|
|
||||||
printProperties()
|
|
||||||
println()
|
|
||||||
children.forEach { (_, node) -> node.printIndented(indent+1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun printProperties() {
|
|
||||||
print("$name ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(child: StNode) {
|
fun add(child: StNode) {
|
||||||
children[child.name] = child
|
children[child.name] = child
|
||||||
child.parent = this
|
child.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val scopedNameList: List<String> by lazy {
|
||||||
|
if(type== StNodeType.GLOBAL)
|
||||||
|
emptyList()
|
||||||
|
else
|
||||||
|
parent.scopedNameList + name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookup(scopedName: List<String>): StNode? {
|
||||||
|
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||||
|
var node = this
|
||||||
|
while(node.type!= StNodeType.GLOBAL)
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
for(name in scopedName) {
|
||||||
|
if(name in node.children)
|
||||||
|
node = node.children.getValue(name)
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StStaticVariable(name: String,
|
class StStaticVariable(name: String,
|
||||||
val dt: DataType,
|
val dt: DataType,
|
||||||
val initialNumericValue: Double?,
|
val bss: Boolean,
|
||||||
val initialStringValue: StString?,
|
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
|
||||||
val initialArrayValue: StArray?,
|
val onetimeInitializationStringValue: StString?,
|
||||||
|
val onetimeInitializationArrayValue: StArray?,
|
||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
val zpwish: ZeropageWish,
|
val zpwish: ZeropageWish,
|
||||||
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
|
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if(bss) {
|
||||||
|
require(onetimeInitializationNumericValue==null)
|
||||||
|
require(onetimeInitializationStringValue==null)
|
||||||
|
require(onetimeInitializationArrayValue.isNullOrEmpty())
|
||||||
|
} else {
|
||||||
|
require(onetimeInitializationNumericValue!=null ||
|
||||||
|
onetimeInitializationStringValue!=null ||
|
||||||
|
onetimeInitializationArrayValue!=null)
|
||||||
|
}
|
||||||
if(length!=null) {
|
if(length!=null) {
|
||||||
require(initialNumericValue == null)
|
require(onetimeInitializationNumericValue == null)
|
||||||
if(initialArrayValue!=null)
|
if(onetimeInitializationArrayValue!=null)
|
||||||
require(length == initialArrayValue.size)
|
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||||
}
|
}
|
||||||
if(initialNumericValue!=null)
|
if(onetimeInitializationNumericValue!=null)
|
||||||
require(dt in NumericDatatypes)
|
require(dt in NumericDatatypes)
|
||||||
if(initialArrayValue!=null)
|
if(onetimeInitializationArrayValue!=null)
|
||||||
require(dt in ArrayDatatypes)
|
require(dt in ArrayDatatypes)
|
||||||
if(initialStringValue!=null) {
|
if(onetimeInitializationStringValue!=null) {
|
||||||
require(dt == DataType.STR)
|
require(dt == DataType.STR)
|
||||||
require(length == initialStringValue.first.length+1)
|
require(length == onetimeInitializationStringValue.first.length+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$name dt=$dt zpw=$zpwish")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
|
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
|
||||||
StNode(name, StNodeType.CONSTANT, position) {
|
StNode(name, StNodeType.CONSTANT, position) {
|
||||||
override fun printProperties() {
|
|
||||||
print("$name dt=$dt value=$value")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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() {
|
|
||||||
print("$name dt=$dt address=${address.toHex()}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StMemorySlab(
|
||||||
|
name: String,
|
||||||
|
val size: UInt,
|
||||||
|
val align: UInt,
|
||||||
|
position: Position
|
||||||
|
):
|
||||||
|
StNode(name, StNodeType.MEMORYSLAB, position) {
|
||||||
|
}
|
||||||
|
|
||||||
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() {
|
|
||||||
print(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StRomSub(name: String, val address: UInt, parameters: List<StSubroutineParameter>, position: Position) :
|
class StRomSub(name: String,
|
||||||
|
val address: UInt,
|
||||||
|
val parameters: List<StRomSubParameter>,
|
||||||
|
val returns: List<RegisterOrStatusflag>,
|
||||||
|
position: Position) :
|
||||||
StNode(name, StNodeType.ROMSUB, position) {
|
StNode(name, StNodeType.ROMSUB, position) {
|
||||||
override fun printProperties() {
|
|
||||||
print("$name address=${address.toHex()}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StSubroutineParameter(val name: String, val type: DataType)
|
class StSubroutineParameter(val name: String, val type: DataType)
|
||||||
class StArrayElement(val number: Double?, val addressOf: List<String>?)
|
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||||
|
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
|
||||||
|
|
||||||
typealias StString = Pair<String, Encoding>
|
typealias StString = Pair<String, Encoding>
|
||||||
typealias StArray = List<StArrayElement>
|
typealias StArray = List<StArrayElement>
|
@ -1,9 +1,11 @@
|
|||||||
package prog8.code.ast
|
package prog8.code.ast
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.core.Position
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
// New (work-in-progress) simplified AST for the code generator.
|
// New simplified AST for the code generator.
|
||||||
|
|
||||||
|
|
||||||
sealed class PtNode(val position: Position) {
|
sealed class PtNode(val position: Position) {
|
||||||
@ -30,6 +32,10 @@ sealed class PtNode(val position: Position) {
|
|||||||
children.add(index, child)
|
children.add(index, child)
|
||||||
child.parent = this
|
child.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||||
|
fun definingSub() = findParentNode<PtSub>(this)
|
||||||
|
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -38,15 +44,17 @@ class PtNodeGroup : PtNode(Position.DUMMY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
|
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||||
val scopedName: List<String> by lazy {
|
// Note that as an exception, the 'name' is not read-only
|
||||||
|
// but a var. This is to allow for cheap node renames.
|
||||||
|
val scopedName: String by lazy {
|
||||||
var namedParent: PtNode = this.parent
|
var namedParent: PtNode = this.parent
|
||||||
if(namedParent is PtProgram)
|
if(namedParent is PtProgram)
|
||||||
listOf(name)
|
name
|
||||||
else {
|
else {
|
||||||
while (namedParent !is PtNamedNode)
|
while (namedParent !is PtNamedNode)
|
||||||
namedParent = namedParent.parent
|
namedParent = namedParent.parent
|
||||||
namedParent.scopedName + name
|
namedParent.scopedName + "." + name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,16 +84,29 @@ class PtProgram(
|
|||||||
class PtBlock(name: String,
|
class PtBlock(name: String,
|
||||||
val address: UInt?,
|
val address: UInt?,
|
||||||
val library: Boolean,
|
val library: Boolean,
|
||||||
|
val forceOutput: Boolean,
|
||||||
|
val alignment: BlockAlignment,
|
||||||
position: Position
|
position: Position
|
||||||
) : PtNamedNode(name, position) {
|
) : PtNamedNode(name, position) {
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print("$name addr=$address library=$library")
|
print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BlockAlignment {
|
||||||
|
NONE,
|
||||||
|
WORD,
|
||||||
|
PAGE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) {
|
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
||||||
override fun printProperties() {}
|
override fun printProperties() {}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
|
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,6 +134,14 @@ class PtNop(position: Position): PtNode(position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtScopeVarsDecls(position: Position): PtNode(position) {
|
// find the parent node of a specific type or interface
|
||||||
override fun printProperties() {}
|
// (useful to figure out in what namespace/block something is defined, etc.)
|
||||||
|
inline fun <reified T> findParentNode(node: PtNode): T? {
|
||||||
|
var candidate = node.parent
|
||||||
|
while(candidate !is T && candidate !is PtProgram)
|
||||||
|
candidate = candidate.parent
|
||||||
|
return if(candidate is PtProgram)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
candidate as T
|
||||||
}
|
}
|
226
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
226
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.Position
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.round
|
||||||
|
|
||||||
|
|
||||||
|
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() {
|
||||||
|
print(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun isSameAs(other: PtExpression): Boolean {
|
||||||
|
return when(this) {
|
||||||
|
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||||
|
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
||||||
|
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||||
|
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||||
|
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||||
|
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||||
|
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||||
|
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
||||||
|
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
|
||||||
|
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
|
||||||
|
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||||
|
val identifier: PtIdentifier
|
||||||
|
get() = children.single() as PtIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
val variable: PtIdentifier
|
||||||
|
get() = children[0] as PtIdentifier
|
||||||
|
val index: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
override fun hashCode(): Int = Objects.hash(children, type)
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtArray)
|
||||||
|
return false
|
||||||
|
return type==other.type && children == other.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBuiltinFunctionCall(val name: String,
|
||||||
|
val void: Boolean,
|
||||||
|
val hasNoSideEffects: Boolean,
|
||||||
|
type: DataType,
|
||||||
|
position: Position) : PtExpression(type, position) {
|
||||||
|
init {
|
||||||
|
if(!void)
|
||||||
|
require(type!=DataType.UNDEFINED)
|
||||||
|
}
|
||||||
|
|
||||||
|
val args: List<PtExpression>
|
||||||
|
get() = children.map { it as PtExpression }
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name void=$void noSideFx=$hasNoSideEffects")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val right: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$operator -> $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||||
|
val element: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val iterable: PtIdentifier
|
||||||
|
get() = children[1] as PtIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtFunctionCall(val name: String,
|
||||||
|
val void: Boolean,
|
||||||
|
type: DataType,
|
||||||
|
position: Position) : PtExpression(type, position) {
|
||||||
|
init {
|
||||||
|
if(!void)
|
||||||
|
require(type!=DataType.UNDEFINED)
|
||||||
|
}
|
||||||
|
|
||||||
|
val args: List<PtExpression>
|
||||||
|
get() = children.map { it as PtExpression }
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name void=$void")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||||
|
val address: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(type==DataType.BOOL)
|
||||||
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
|
if(type!=DataType.FLOAT) {
|
||||||
|
val rounded = round(number)
|
||||||
|
if (rounded != number)
|
||||||
|
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$number ($type)")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = Objects.hash(type, number)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtNumber)
|
||||||
|
return false
|
||||||
|
return number==other.number
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
val value: 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
|
||||||
|
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print(operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtRange(type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
val from: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val to: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
val step: PtNumber
|
||||||
|
get() = children[2] as PtNumber
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$encoding:\"$value\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = Objects.hash(value, encoding)
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtString)
|
||||||
|
return false
|
||||||
|
return value==other.value && encoding == other.encoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
val value: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// special node that isn't created from compiling user code, but used internally
|
||||||
|
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("reg=$register $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||||
|
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
|
153
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
153
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
||||||
|
* passing it as a String to the specified receiver function.
|
||||||
|
*/
|
||||||
|
fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
||||||
|
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||||
|
fun txt(node: PtNode): String {
|
||||||
|
return when(node) {
|
||||||
|
is PtAssignTarget -> ""
|
||||||
|
is PtAssignment -> "<assign>"
|
||||||
|
is PtBreakpoint -> "%breakpoint"
|
||||||
|
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||||
|
is PtAddressOf -> "&"
|
||||||
|
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||||
|
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
|
||||||
|
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
val str = if(node.void) "void " else ""
|
||||||
|
str + node.name + "()"
|
||||||
|
}
|
||||||
|
is PtContainmentCheck -> "in"
|
||||||
|
is PtFunctionCall -> {
|
||||||
|
val str = if(node.void) "void " else ""
|
||||||
|
str + node.name + "()"
|
||||||
|
}
|
||||||
|
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||||
|
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
||||||
|
is PtMemoryByte -> "@()"
|
||||||
|
is PtNumber -> "${node.number.toHex()} ${type(node.type)}"
|
||||||
|
is PtPrefix -> node.operator
|
||||||
|
is PtRange -> "<range>"
|
||||||
|
is PtString -> "\"${node.value.escape()}\""
|
||||||
|
is PtTypeCast -> "as ${node.type.name.lowercase()}"
|
||||||
|
is PtForLoop -> "for"
|
||||||
|
is PtIfElse -> "ifelse"
|
||||||
|
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
|
||||||
|
is PtInlineAssembly -> {
|
||||||
|
if(node.isIR)
|
||||||
|
"%ir {{ ...${node.assembly.length} characters... }}"
|
||||||
|
else
|
||||||
|
"%asm {{ ...${node.assembly.length} characters... }}"
|
||||||
|
}
|
||||||
|
is PtJump -> {
|
||||||
|
if(node.identifier!=null)
|
||||||
|
"goto ${node.identifier.name}"
|
||||||
|
else if(node.address!=null)
|
||||||
|
"goto ${node.address.toHex()}"
|
||||||
|
else if(node.generatedLabel!=null)
|
||||||
|
"goto ${node.generatedLabel}"
|
||||||
|
else
|
||||||
|
"???"
|
||||||
|
}
|
||||||
|
is PtAsmSub -> {
|
||||||
|
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||||
|
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||||
|
val returns = if (node.returnTypes.isEmpty()) "" else (if (node.returnTypes.size == 1) "-> ${node.returnTypes[0].name.lowercase()}" else "-> ${node.returnTypes.map { it.name.lowercase() }}")
|
||||||
|
val str = if (node.inline) "inline " else ""
|
||||||
|
if(node.address==null) {
|
||||||
|
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||||
|
} else {
|
||||||
|
str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtBlock -> {
|
||||||
|
val addr = if(node.address==null) "" else "@${node.address?.toHex()}"
|
||||||
|
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
|
||||||
|
"\nblock '${node.name}' $addr $align"
|
||||||
|
}
|
||||||
|
is PtConstant -> {
|
||||||
|
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
|
||||||
|
"const ${node.type.name.lowercase()} ${node.name} = $value"
|
||||||
|
}
|
||||||
|
is PtLabel -> "${node.name}:"
|
||||||
|
is PtMemMapped -> {
|
||||||
|
if(node.type in ArrayDatatypes) {
|
||||||
|
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
|
||||||
|
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||||
|
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
|
||||||
|
} else {
|
||||||
|
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtSub -> {
|
||||||
|
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||||
|
var str = if(node.inline) "inline " else ""
|
||||||
|
str += "sub ${node.name}($params) "
|
||||||
|
if(node.returntype!=null)
|
||||||
|
str += "-> ${node.returntype.name.lowercase()}"
|
||||||
|
str
|
||||||
|
}
|
||||||
|
is PtVariable -> {
|
||||||
|
val str = if(node.arraySize!=null) {
|
||||||
|
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||||
|
"${eltType.name.lowercase()}[${node.arraySize}] ${node.name}"
|
||||||
|
}
|
||||||
|
else if(node.type in ArrayDatatypes) {
|
||||||
|
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||||
|
"${eltType.name.lowercase()}[] ${node.name}"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
"${node.type.name.lowercase()} ${node.name}"
|
||||||
|
if(node.value!=null)
|
||||||
|
str + " = " + txt(node.value!!)
|
||||||
|
else
|
||||||
|
str
|
||||||
|
}
|
||||||
|
is PtNodeGroup -> "<group>"
|
||||||
|
is PtNop -> "nop"
|
||||||
|
is PtPostIncrDecr -> "<post> ${node.operator}"
|
||||||
|
is PtProgram -> "PROGRAM ${node.name}"
|
||||||
|
is PtRepeatLoop -> "repeat"
|
||||||
|
is PtReturn -> "return"
|
||||||
|
is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}"
|
||||||
|
is PtWhen -> "when"
|
||||||
|
is PtWhenChoice -> {
|
||||||
|
if(node.isElse)
|
||||||
|
"else"
|
||||||
|
else
|
||||||
|
"->"
|
||||||
|
}
|
||||||
|
else -> throw InternalCompilerException("unrecognised ast node $node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(root is PtProgram) {
|
||||||
|
output(txt(root))
|
||||||
|
root.children.forEach {
|
||||||
|
walkAst(it) { node, depth ->
|
||||||
|
val txt = txt(node)
|
||||||
|
if(txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
walkAst(root) { node, depth ->
|
||||||
|
val txt = txt(node)
|
||||||
|
if(txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
|
||||||
|
fun recurse(node: PtNode, depth: Int) {
|
||||||
|
act(node, depth)
|
||||||
|
node.children.forEach { recurse(it, depth+1) }
|
||||||
|
}
|
||||||
|
recurse(root, 0)
|
||||||
|
}
|
@ -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>, // TODO join with register as Pairs ?
|
||||||
val retvalRegisters: List<RegisterOrStatusflag>,
|
val retvalRegisters: List<RegisterOrStatusflag>,
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
position: Position
|
position: Position
|
||||||
@ -28,6 +29,14 @@ class PtSub(
|
|||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print(name)
|
print(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
// params and return value should not be str
|
||||||
|
if(parameters.any{ it.type !in NumericDatatypes })
|
||||||
|
throw AssemblyError("non-numeric parameter")
|
||||||
|
if(returntype!=null && returntype !in NumericDatatypes)
|
||||||
|
throw AssemblyError("non-numeric returntype $returntype")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -38,18 +47,43 @@ class PtSubroutineParameter(val name: String, val type: DataType, position: Posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtAssignment(val augmentable: Boolean, position: Position) : PtNode(position) {
|
class PtAssignment(position: Position) : PtNode(position) {
|
||||||
val target: PtAssignTarget
|
val target: PtAssignTarget
|
||||||
get() = children[0] as PtAssignTarget
|
get() = children[0] as PtAssignTarget
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
override fun printProperties() {
|
override fun printProperties() { }
|
||||||
print("aug=$augmentable")
|
|
||||||
|
val isInplaceAssign: Boolean by lazy {
|
||||||
|
val target = target.children.single() as PtExpression
|
||||||
|
when(val source = value) {
|
||||||
|
is PtArrayIndexer -> {
|
||||||
|
if(target is PtArrayIndexer && source.type==target.type) {
|
||||||
|
if(target.variable isSameAs source.variable) {
|
||||||
|
target.index isSameAs source.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.name==source.name
|
||||||
|
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
|
||||||
|
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
|
||||||
|
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
|
||||||
|
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
|
||||||
|
is PtPrefix -> {
|
||||||
|
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
|
||||||
|
||
|
||||||
|
(target is PtIdentifier && (source.value as? PtIdentifier)?.name==target.name)
|
||||||
|
}
|
||||||
|
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
|
||||||
|
is PtBinaryExpression ->
|
||||||
|
target isSameAs source.left
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||||
val identifier: PtIdentifier?
|
val identifier: PtIdentifier?
|
||||||
get() = children.single() as? PtIdentifier
|
get() = children.single() as? PtIdentifier
|
||||||
@ -62,9 +96,9 @@ class PtAssignTarget(position: Position) : PtNode(position) {
|
|||||||
get() {
|
get() {
|
||||||
return when(val tgt = children.single()) {
|
return when(val tgt = children.single()) {
|
||||||
is PtIdentifier -> tgt.type
|
is PtIdentifier -> tgt.type
|
||||||
is PtArrayIndexer -> tgt.type // TODO array to elt type?
|
is PtArrayIndexer -> tgt.type
|
||||||
is PtMemoryByte -> tgt.type
|
is PtMemoryByte -> tgt.type
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird target $tgt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,8 +131,8 @@ class PtForLoop(position: Position) : PtNode(position) {
|
|||||||
|
|
||||||
|
|
||||||
class PtIfElse(position: Position) : PtNode(position) {
|
class PtIfElse(position: Position) : PtNode(position) {
|
||||||
val condition: PtExpression
|
val condition: PtBinaryExpression
|
||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtBinaryExpression
|
||||||
val ifScope: PtNodeGroup
|
val ifScope: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
val elseScope: PtNodeGroup
|
val elseScope: PtNodeGroup
|
||||||
@ -168,7 +202,7 @@ class PtConstant(name: String, val type: DataType, val value: Double, position:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtMemMapped(name: String, val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position) {
|
class PtMemMapped(name: String, val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
|
||||||
override fun printProperties() {
|
override fun printProperties() {
|
||||||
print("&$type $name = ${address.toHex()}")
|
print("&$type $name = ${address.toHex()}")
|
||||||
}
|
}
|
@ -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()
|
||||||
)
|
)
|
||||||
|
89
codeCore/src/prog8/code/core/Conversions.kt
Normal file
89
codeCore/src/prog8/code/core/Conversions.kt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
fun Number.toHex(): String {
|
||||||
|
// 0..15 -> "0".."15"
|
||||||
|
// 16..255 -> "$10".."$ff"
|
||||||
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
// negative values are prefixed with '-'.
|
||||||
|
val integer = this.toInt()
|
||||||
|
if(integer<0)
|
||||||
|
return '-' + abs(integer).toHex()
|
||||||
|
return when (integer) {
|
||||||
|
in 0 until 16 -> integer.toString()
|
||||||
|
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||||
|
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
||||||
|
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UInt.toHex(): String {
|
||||||
|
// 0..15 -> "0".."15"
|
||||||
|
// 16..255 -> "$10".."$ff"
|
||||||
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
return when (this) {
|
||||||
|
in 0u until 16u -> this.toString()
|
||||||
|
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
|
||||||
|
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
|
||||||
|
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Char.escape(): Char = this.toString().escape()[0]
|
||||||
|
|
||||||
|
fun String.escape(): String {
|
||||||
|
val es = this.map {
|
||||||
|
when(it) {
|
||||||
|
'\t' -> "\\t"
|
||||||
|
'\n' -> "\\n"
|
||||||
|
'\r' -> "\\r"
|
||||||
|
'"' -> "\\\""
|
||||||
|
in '\u8000'..'\u80ff' -> "\\x" + (it.code - 0x8000).toString(16).padStart(2, '0') // 'ugly' passthrough hack
|
||||||
|
in '\u0000'..'\u00ff' -> it.toString()
|
||||||
|
else -> "\\u" + it.code.toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return es.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.unescape(): String {
|
||||||
|
val result = mutableListOf<Char>()
|
||||||
|
val iter = this.iterator()
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
val c = iter.nextChar()
|
||||||
|
if(c=='\\') {
|
||||||
|
val ec = iter.nextChar()
|
||||||
|
result.add(when(ec) {
|
||||||
|
'\\' -> '\\'
|
||||||
|
'n' -> '\n'
|
||||||
|
'r' -> '\r'
|
||||||
|
'"' -> '"'
|
||||||
|
'\'' -> '\''
|
||||||
|
'u' -> {
|
||||||
|
try {
|
||||||
|
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||||
|
} catch (sb: StringIndexOutOfBoundsException) {
|
||||||
|
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||||
|
} catch (nf: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'x' -> {
|
||||||
|
try {
|
||||||
|
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||||
|
(0x8000 + hex).toChar() // 'ugly' pass-through hack
|
||||||
|
} catch (sb: StringIndexOutOfBoundsException) {
|
||||||
|
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||||
|
} catch (nf: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("invalid escape char in string: \\$ec")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.add(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.joinToString("")
|
||||||
|
}
|
@ -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 {
|
||||||
@ -102,31 +99,33 @@ enum class Statusflag {
|
|||||||
enum class BranchCondition {
|
enum class BranchCondition {
|
||||||
CS,
|
CS,
|
||||||
CC,
|
CC,
|
||||||
EQ,
|
EQ, // EQ == Z
|
||||||
Z,
|
Z,
|
||||||
NE,
|
NE, // NE == NZ
|
||||||
NZ,
|
NZ,
|
||||||
|
MI, // MI == NEG
|
||||||
|
NEG,
|
||||||
|
PL, // PL == POS
|
||||||
|
POS,
|
||||||
VS,
|
VS,
|
||||||
VC,
|
VC,
|
||||||
MI,
|
|
||||||
NEG,
|
|
||||||
PL,
|
|
||||||
POS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||||
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
|
||||||
@ -136,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,18 +13,25 @@ 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>
|
|
||||||
var zeropage: Zeropage
|
|
||||||
val cpu: CpuType
|
val cpu: CpuType
|
||||||
|
var zeropage: Zeropage
|
||||||
|
var golden: GoldenRam
|
||||||
|
|
||||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
fun initializeMemoryAreas(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
|
||||||
|
require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,31 @@ import com.github.michaelbull.result.Ok
|
|||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
|
|
||||||
|
|
||||||
class ZeropageAllocationError(message: String) : Exception(message)
|
class MemAllocationError(message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
||||||
|
data class VarAllocation(val address: UInt, val dt: DataType, val size: Int)
|
||||||
|
|
||||||
|
abstract fun allocate(name: List<String>,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||||
|
|
||||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||||
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
|
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
|
||||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int)
|
|
||||||
|
|
||||||
// the variables allocated into Zeropage.
|
// the variables allocated into Zeropage.
|
||||||
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||||
val allocatedVariables = mutableMapOf<List<String>, ZpAllocation>()
|
val allocatedVariables = mutableMapOf<List<String>, VarAllocation>()
|
||||||
|
|
||||||
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
@ -41,17 +51,16 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
return free.windowed(2).any { it[0] == it[1] - 1u }
|
return free.windowed(2).any { it[0] == it[1] - 1u }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allocate(name: List<String>,
|
override fun allocate(name: List<String>,
|
||||||
datatype: DataType,
|
datatype: DataType,
|
||||||
numElements: Int?,
|
numElements: Int?,
|
||||||
position: Position?,
|
position: Position?,
|
||||||
errors: IErrorReporter
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||||
): Result<Pair<UInt, Int>, ZeropageAllocationError> {
|
|
||||||
|
|
||||||
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
||||||
|
|
||||||
if(options.zeropage== ZeropageType.DONTUSE)
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
return Err(ZeropageAllocationError("zero page usage has been disabled"))
|
return Err(MemAllocationError("zero page usage has been disabled"))
|
||||||
|
|
||||||
val size: Int =
|
val size: Int =
|
||||||
when (datatype) {
|
when (datatype) {
|
||||||
@ -72,9 +81,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
else
|
else
|
||||||
errors.warn("$name: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
|
errors.warn("$name: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
|
||||||
memsize
|
memsize
|
||||||
} else return Err(ZeropageAllocationError("floating point option not enabled"))
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
}
|
}
|
||||||
else -> return Err(ZeropageAllocationError("cannot put datatype $datatype in zeropage"))
|
else -> throw MemAllocationError("weird dt")
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
@ -82,18 +91,18 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
if(size==1) {
|
if(size==1) {
|
||||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
if(oneSeparateByteFree(candidate))
|
if(oneSeparateByteFree(candidate))
|
||||||
return Ok(Pair(makeAllocation(candidate, 1, datatype, name), 1))
|
return Ok(VarAllocation(makeAllocation(candidate, 1, datatype, name), datatype,1))
|
||||||
}
|
}
|
||||||
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 1))
|
return Ok(VarAllocation(makeAllocation(free[0], 1, datatype, name), datatype,1))
|
||||||
}
|
}
|
||||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
if (sequentialFree(candidate, size))
|
if (sequentialFree(candidate, size))
|
||||||
return Ok(Pair(makeAllocation(candidate, size, datatype, name), size))
|
return Ok(VarAllocation(makeAllocation(candidate, size, datatype, name), datatype, size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(ZeropageAllocationError("no more free space in ZP to allocate $size sequential bytes"))
|
return Err(MemAllocationError("no more free space in ZP to allocate $size sequential bytes"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reserve(range: UIntRange) = free.removeAll(range)
|
private fun reserve(range: UIntRange) = free.removeAll(range)
|
||||||
@ -103,9 +112,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
free.removeAll(address until address+size.toUInt())
|
free.removeAll(address until address+size.toUInt())
|
||||||
if(name.isNotEmpty()) {
|
if(name.isNotEmpty()) {
|
||||||
allocatedVariables[name] = when(datatype) {
|
allocatedVariables[name] = when(datatype) {
|
||||||
in NumericDatatypes -> ZpAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||||
DataType.STR -> ZpAllocation(address, datatype, size)
|
DataType.STR -> VarAllocation(address, datatype, size)
|
||||||
in ArrayDatatypes -> ZpAllocation(address, datatype, size)
|
in ArrayDatatypes -> VarAllocation(address, datatype, size)
|
||||||
else -> throw AssemblyError("invalid dt")
|
else -> throw AssemblyError("invalid dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,4 +126,40 @@ 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||||
|
private var nextLocation: UInt = region.first
|
||||||
|
|
||||||
|
override fun allocate(
|
||||||
|
name: List<String>,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||||
|
|
||||||
|
val size: Int =
|
||||||
|
when (datatype) {
|
||||||
|
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||||
|
DataType.STR, in ArrayDatatypes -> {
|
||||||
|
options.compTarget.memorySize(datatype, numElements!!)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
if (options.floats) {
|
||||||
|
options.compTarget.memorySize(DataType.FLOAT)
|
||||||
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
|
}
|
||||||
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(nextLocation<=region.last && (region.last + 1u - nextLocation) >= size.toUInt()) {
|
||||||
|
val result = Ok(VarAllocation(nextLocation, datatype, size))
|
||||||
|
nextLocation += size.toUInt()
|
||||||
|
result
|
||||||
|
} else
|
||||||
|
Err(MemAllocationError("no more free space in Golden RAM to allocate $size sequential bytes"))
|
||||||
|
}
|
||||||
}
|
}
|
19
codeCore/src/prog8/code/core/Operators.kt
Normal file
19
codeCore/src/prog8/code/core/Operators.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||||
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
|
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||||
|
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||||
|
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||||
|
|
||||||
|
fun invertedComparisonOperator(operator: String) =
|
||||||
|
when (operator) {
|
||||||
|
"==" -> "!="
|
||||||
|
"!=" -> "=="
|
||||||
|
"<" -> ">="
|
||||||
|
">" -> "<="
|
||||||
|
"<=" -> ">"
|
||||||
|
">=" -> "<"
|
||||||
|
else -> null
|
||||||
|
}
|
@ -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>"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ sealed class SourceCode {
|
|||||||
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
||||||
*/
|
*/
|
||||||
class Resource(pathString: String): SourceCode() {
|
class Resource(pathString: String): SourceCode() {
|
||||||
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
|
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
|
||||||
|
|
||||||
override val isFromResources = true
|
override val isFromResources = true
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
@ -125,7 +125,7 @@ sealed class SourceCode {
|
|||||||
}
|
}
|
||||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||||
text = stream!!.reader().use { it.readText() }
|
text = stream!!.reader().use { it.readText() }
|
||||||
name = Path.of(pathString).toFile().nameWithoutExtension
|
name = Path(pathString).toFile().nameWithoutExtension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package prog8.code.core
|
|
||||||
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
fun Number.toHex(): String {
|
|
||||||
// 0..15 -> "0".."15"
|
|
||||||
// 16..255 -> "$10".."$ff"
|
|
||||||
// 256..65536 -> "$0100".."$ffff"
|
|
||||||
// negative values are prefixed with '-'.
|
|
||||||
val integer = this.toInt()
|
|
||||||
if(integer<0)
|
|
||||||
return '-' + abs(integer).toHex()
|
|
||||||
return when (integer) {
|
|
||||||
in 0 until 16 -> integer.toString()
|
|
||||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
|
||||||
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
|
||||||
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun UInt.toHex(): String {
|
|
||||||
// 0..15 -> "0".."15"
|
|
||||||
// 16..255 -> "$10".."$ff"
|
|
||||||
// 256..65536 -> "$0100".."$ffff"
|
|
||||||
return when (this) {
|
|
||||||
in 0u until 16u -> this.toString()
|
|
||||||
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
|
|
||||||
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
|
|
||||||
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.code.target.atari
|
package prog8.code.target.atari
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.c64.normal6502instructions
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
@ -15,12 +14,13 @@ 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 lateinit var golden: GoldenRam
|
||||||
|
|
||||||
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)
|
||||||
@ -57,9 +57,8 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = AtariZeropage(compilerOptions)
|
zeropage = AtariZeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val opcodeNames = normal6502instructions
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.code.target.c128
|
package prog8.code.target.c128
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.c64.normal6502instructions
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -16,12 +15,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 lateinit var golden: GoldenRam
|
||||||
|
|
||||||
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)
|
||||||
@ -47,9 +46,8 @@ class C128MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = C128Zeropage(compilerOptions)
|
zeropage = C128Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||||
}
|
}
|
||||||
|
|
||||||
override val opcodeNames = normal6502instructions
|
|
||||||
}
|
}
|
||||||
|
@ -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 lateinit var golden: GoldenRam
|
||||||
|
|
||||||
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)
|
||||||
@ -55,22 +55,9 @@ class C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = C64Zeropage(compilerOptions)
|
zeropage = C64Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val opcodeNames = normal6502instructions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
|
||||||
internal val normal6502instructions = setOf(
|
|
||||||
"adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
|
||||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
|
||||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
|
|
||||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
|
||||||
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
|
|
||||||
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
|
|
||||||
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
|
|
||||||
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
|
|
||||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
|
@ -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}")] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}s")] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}L")] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}H")] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sL")] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sH")] = VarAllocation((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 lateinit var golden: GoldenRam
|
||||||
|
|
||||||
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")
|
||||||
@ -50,28 +50,16 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
println("\nStarting Commander X16 emulator $emulator...")
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||||
val process: Process = processb.start()
|
val process: Process = processb.start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = CX16Zeropage(compilerOptions)
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 65c02 opcodes, these cannot be used as variable or label names
|
|
||||||
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
|
|
||||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
|
||||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
|
|
||||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
|
||||||
"inc", "inx", "iny", "jmp", "jsr",
|
|
||||||
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
|
|
||||||
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
|
|
||||||
"sec", "sed", "sei",
|
|
||||||
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
|
|
||||||
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
|
|
||||||
"rmb", "smb", "stp", "wai")
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,18 +43,27 @@ 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.
|
}
|
||||||
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}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
override fun allocateCx16VirtualRegisters() {
|
||||||
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
for(reg in 0..15) {
|
||||||
}
|
allocatedVariables[listOf("cx16", "r${reg}")] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}s")] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}L")] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}H")] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sL")] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sH")] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,10 @@
|
|||||||
package prog8.code.target.virtual
|
package prog8.code.target.virtual
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.CpuType
|
|
||||||
import prog8.code.core.IMachineDefinition
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.vm.Assembler
|
|
||||||
import prog8.vm.Memory
|
|
||||||
import prog8.vm.VirtualMachine
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.isReadable
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
class VirtualMachineDefinition: IMachineDefinition {
|
class VirtualMachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
@ -17,15 +12,15 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||||
override val FLOAT_MEM_SIZE = 4
|
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 golden: GoldenRam // not actually used
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage // not actually used
|
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
||||||
|
|
||||||
override fun getFloat(num: Number) = TODO("float 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")
|
||||||
@ -33,19 +28,25 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
println("\nStarting Virtual Machine...")
|
println("\nStarting Virtual Machine...")
|
||||||
val source = File("$programNameWithPath.p8virt").readText()
|
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||||
val (memsrc, programsrc) = source.split("------PROGRAM------".toRegex(), 2)
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
val memory = Memory()
|
val filename = programNameWithPath.name
|
||||||
val assembler = Assembler()
|
if(programNameWithPath.isReadable()) {
|
||||||
assembler.initializeMemory(memsrc, memory)
|
vm.runProgram(programNameWithPath.readText())
|
||||||
val program = assembler.assembleProgram(programsrc)
|
} else {
|
||||||
val vm = VirtualMachine(memory, program)
|
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||||
vm.run(throttle = true)
|
if(withExt.isReadable())
|
||||||
|
vm.runProgram(withExt.readText())
|
||||||
|
else
|
||||||
|
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = false
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {}
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {}
|
||||||
|
}
|
||||||
override val opcodeNames = emptySet<String>()
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(irSource: String)
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,11 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':codeAst')
|
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
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"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
<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="module" module-name="codeAst" />
|
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="compilerAst" />
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.ast.*
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.ParentSentinel
|
||||||
|
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.statements.*
|
import prog8.ast.statements.*
|
||||||
@ -10,13 +13,11 @@ 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.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import prog8.code.core.SourceCode
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.io.path.Path
|
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"
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ class AsmGen(internal val program: Program,
|
|||||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
|
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
|
||||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
|
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
|
|
||||||
@ -71,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('\r', '\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
|
||||||
@ -106,13 +100,13 @@ class AsmGen(internal val program: Program,
|
|||||||
fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource)
|
fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource)
|
||||||
fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource)
|
fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource)
|
||||||
|
|
||||||
internal fun getTempVarName(dt: DataType): List<String> {
|
internal fun getTempVarName(dt: DataType): String {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
DataType.UBYTE -> listOf("cx16", "r9L")
|
DataType.UBYTE -> "cx16.r9L"
|
||||||
DataType.BYTE -> listOf("cx16", "r9sL")
|
DataType.BYTE -> "cx16.r9sL"
|
||||||
DataType.UWORD -> listOf("cx16", "r9")
|
DataType.UWORD -> "cx16.r9"
|
||||||
DataType.WORD -> listOf("cx16", "r9s")
|
DataType.WORD -> "cx16.r9s"
|
||||||
DataType.FLOAT -> listOf("floats", "tempvar_swap_float") // defined in floats.p8
|
DataType.FLOAT -> TODO("no temporary float var available")
|
||||||
else -> throw FatalAstException("invalid dt $dt")
|
else -> throw FatalAstException("invalid dt $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,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)
|
||||||
@ -332,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")
|
||||||
@ -383,7 +375,7 @@ class AsmGen(internal val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
require(options.compTarget.memorySize(DataType.FLOAT) == 5)
|
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
|
||||||
out(
|
out(
|
||||||
"""
|
"""
|
||||||
lda $indexName
|
lda $indexName
|
||||||
@ -414,7 +406,7 @@ class AsmGen(internal val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
require(options.compTarget.memorySize(DataType.FLOAT) == 5)
|
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
|
||||||
out(
|
out(
|
||||||
"""
|
"""
|
||||||
lda $indexName
|
lda $indexName
|
||||||
@ -442,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(name: String, singleArg:AsmAssignSource, scope: Subroutine): DataType =
|
|
||||||
builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, false, scope)
|
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCallStatement(name: String, singleArg: AsmAssignSource, scope: Subroutine) =
|
|
||||||
builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, 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)
|
||||||
|
|
||||||
@ -548,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)
|
||||||
@ -556,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)
|
||||||
@ -573,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) {
|
||||||
@ -625,8 +605,8 @@ 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) { "invalid repeat count ${stmt.position}" }
|
||||||
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("""
|
||||||
@ -667,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
|
||||||
@ -687,8 +667,8 @@ $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) { "invalid repeat count ${stmt.position}" }
|
||||||
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")
|
||||||
@ -706,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")
|
||||||
@ -743,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)
|
||||||
@ -758,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)
|
||||||
@ -769,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)
|
||||||
@ -814,7 +794,7 @@ $repeatLabel lda $counterVar
|
|||||||
if(stmt.truepart.isEmpty() && stmt.elsepart.isNotEmpty())
|
if(stmt.truepart.isEmpty() && stmt.elsepart.isNotEmpty())
|
||||||
throw AssemblyError("only else part contains code, shoud have been switched already")
|
throw AssemblyError("only else part contains code, shoud have been switched already")
|
||||||
|
|
||||||
val jump = stmt.truepart.statements.first() as? Jump
|
val jump = stmt.truepart.statements.firstOrNull() as? Jump
|
||||||
if(jump!=null) {
|
if(jump!=null) {
|
||||||
// branch with only a jump (goto)
|
// branch with only a jump (goto)
|
||||||
val instruction = branchInstruction(stmt.condition, false)
|
val instruction = branchInstruction(stmt.condition, false)
|
||||||
@ -832,15 +812,17 @@ $repeatLabel lda $counterVar
|
|||||||
translate(stmt.elsepart)
|
translate(stmt.elsepart)
|
||||||
} else {
|
} else {
|
||||||
if(stmt.elsepart.isEmpty()) {
|
if(stmt.elsepart.isEmpty()) {
|
||||||
val instruction = branchInstruction(stmt.condition, true)
|
if(stmt.truepart.isNotEmpty()) {
|
||||||
val elseLabel = makeLabel("branch_else")
|
val instruction = branchInstruction(stmt.condition, true)
|
||||||
out(" $instruction $elseLabel")
|
val elseLabel = program.makeLabel("branch_else")
|
||||||
translate(stmt.truepart)
|
out(" $instruction $elseLabel")
|
||||||
out(elseLabel)
|
translate(stmt.truepart)
|
||||||
|
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)
|
||||||
@ -858,7 +840,7 @@ $repeatLabel lda $counterVar
|
|||||||
if(stmt.definingModule.source is SourceCode.Generated)
|
if(stmt.definingModule.source is SourceCode.Generated)
|
||||||
throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported")
|
throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported")
|
||||||
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
|
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
|
||||||
success = { assemblyLines.add(it.trimEnd().trimStart('\n')) },
|
success = { assemblyLines.add(it.trimEnd().trimStart('\r', '\n')) },
|
||||||
failure = { errors.err(it.toString(), stmt.position) }
|
failure = { errors.err(it.toString(), stmt.position) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -885,18 +867,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
|
||||||
@ -916,8 +886,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!!
|
||||||
@ -943,11 +911,10 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(asm: InlineAssembly) {
|
private fun translate(asm: InlineAssembly) {
|
||||||
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||||
assemblyLines.add(assembly)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun returnRegisterOfFunction(it: IdentifierReference, argumentTypesForBuiltinFunc: List<DataType>?): RegisterOrPair {
|
internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair {
|
||||||
return when (val targetRoutine = it.targetStatement(program)!!) {
|
return when (val targetRoutine = it.targetStatement(program)!!) {
|
||||||
is BuiltinFunctionPlaceholder -> {
|
is BuiltinFunctionPlaceholder -> {
|
||||||
val func = BuiltinFunctions.getValue(targetRoutine.name)
|
val func = BuiltinFunctions.getValue(targetRoutine.name)
|
||||||
@ -956,16 +923,11 @@ $repeatLabel lda $counterVar
|
|||||||
in WordDatatypes -> RegisterOrPair.AY
|
in WordDatatypes -> RegisterOrPair.AY
|
||||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
DataType.FLOAT -> RegisterOrPair.FAC1
|
||||||
else -> {
|
else -> {
|
||||||
if(!func.hasReturn)
|
when(builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }) {
|
||||||
throw AssemblyError("func has no returntype")
|
in ByteDatatypes -> RegisterOrPair.A
|
||||||
else {
|
in WordDatatypes -> RegisterOrPair.AY
|
||||||
val args = argumentTypesForBuiltinFunc!!.map { defaultZero(it, Position.DUMMY) }
|
DataType.FLOAT -> RegisterOrPair.FAC1
|
||||||
when(builtinFunctionReturnType(func.name, args, program).getOrElse { DataType.UNDEFINED }) {
|
else -> throw AssemblyError("weird returntype")
|
||||||
in ByteDatatypes -> RegisterOrPair.A
|
|
||||||
in WordDatatypes -> RegisterOrPair.AY
|
|
||||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
|
||||||
else -> throw AssemblyError("weird returntype")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1108,11 +1070,20 @@ $repeatLabel lda $counterVar
|
|||||||
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||||
if(saveA)
|
if(saveA)
|
||||||
out(" pha")
|
out(" pha")
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(ptrAndIndex.second.isSimple) {
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
if(saveA)
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
out(" pla")
|
if(saveA)
|
||||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
out(" pla")
|
||||||
|
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
|
||||||
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, true)
|
||||||
|
if(saveA)
|
||||||
|
out(" pla")
|
||||||
|
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(pointervar!=null && isZpVar(pointervar)) {
|
if(pointervar!=null && isZpVar(pointervar)) {
|
||||||
@ -1120,9 +1091,16 @@ $repeatLabel lda $counterVar
|
|||||||
out(" lda (${asmSymbolName(pointervar)}),y")
|
out(" lda (${asmSymbolName(pointervar)}),y")
|
||||||
} else {
|
} else {
|
||||||
// copy the pointer var to zp first
|
// copy the pointer var to zp first
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(ptrAndIndex.second.isSimple) {
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
|
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
|
||||||
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -1183,14 +1161,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")
|
||||||
@ -1270,7 +1248,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")
|
||||||
@ -1616,8 +1594,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1651,8 +1635,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1688,8 +1678,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1727,8 +1724,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.WORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1767,8 +1771,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1804,8 +1814,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.BYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1847,8 +1863,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2")
|
return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1891,8 +1914,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleLeftOperand(left, right, ::code))
|
if(wordJumpForSimpleLeftOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(right.isSimple) {
|
||||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.WORD, right)
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1932,8 +1962,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1969,8 +2005,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.BYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2014,8 +2056,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2062,8 +2111,15 @@ $repeatLabel lda $counterVar
|
|||||||
return code(asmVariableName(left))
|
return code(asmVariableName(left))
|
||||||
}
|
}
|
||||||
|
|
||||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(right.isSimple) {
|
||||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.WORD, right)
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2099,8 +2155,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2133,8 +2195,14 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.BYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
|
||||||
|
out(" pla")
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2169,8 +2237,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(right.isSimple) {
|
||||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, right)
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2208,8 +2283,15 @@ $repeatLabel lda $counterVar
|
|||||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.WORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2244,8 +2326,17 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else if(right.isSimple) {
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel")
|
out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2281,8 +2372,17 @@ $repeatLabel lda $counterVar
|
|||||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||||
return
|
return
|
||||||
|
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
|
} else if(right.isSimple) {
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UBYTE, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
return code("P8ZP_SCRATCH_B1")
|
return code("P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2348,8 +2448,18 @@ $repeatLabel lda $counterVar
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else if(right.isSimple) {
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
out("""
|
out("""
|
||||||
cmp P8ZP_SCRATCH_W2
|
cmp P8ZP_SCRATCH_W2
|
||||||
bne $jumpIfFalseLabel
|
bne $jumpIfFalseLabel
|
||||||
@ -2424,8 +2534,18 @@ $repeatLabel lda $counterVar
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
if(left.isSimple) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
|
} else if (right.isSimple) {
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||||
|
} else {
|
||||||
|
pushCpuStack(DataType.UWORD, left)
|
||||||
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
}
|
||||||
out("""
|
out("""
|
||||||
cmp P8ZP_SCRATCH_W2
|
cmp P8ZP_SCRATCH_W2
|
||||||
bne +
|
bne +
|
||||||
@ -2815,120 +2935,12 @@ $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, listOf(valueDt))
|
|
||||||
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): unary function calls taking a single param and producing a value.
|
|
||||||
// directly assign their argument from the previous call's returnvalue.
|
|
||||||
segments.dropLast(1).forEach {
|
|
||||||
it as IFunctionCall
|
|
||||||
valueDt = translateUnaryFunctionCallWithArgSource(it.target, valueSource, false, subroutine)
|
|
||||||
val resultReg = returnRegisterOfFunction(it.target, listOf(valueDt))
|
|
||||||
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
|
||||||
}
|
|
||||||
// the last segment: unary function call taking a single param and optionally producing a result value.
|
|
||||||
val lastCall = segments.last() as IFunctionCall
|
|
||||||
if(isStatement) {
|
|
||||||
translateUnaryFunctionCallWithArgSource(lastCall.target, valueSource, true, subroutine)
|
|
||||||
} else {
|
|
||||||
valueDt = translateUnaryFunctionCallWithArgSource(lastCall.target, 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 translateUnaryFunctionCallWithArgSource(target: IdentifierReference, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
|
||||||
when(val targetStmt = target.targetStatement(program)!!) {
|
|
||||||
is BuiltinFunctionPlaceholder -> {
|
|
||||||
return if(isStatement) {
|
|
||||||
translateBuiltinFunctionCallStatement(targetStmt.name, singleArg, scope)
|
|
||||||
DataType.UNDEFINED
|
|
||||||
} else {
|
|
||||||
translateBuiltinFunctionCallExpression(targetStmt.name, singleArg, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Subroutine -> {
|
|
||||||
val argDt = targetStmt.parameters.single().type
|
|
||||||
if(targetStmt.isAsmSubroutine) {
|
|
||||||
// argument via registers
|
|
||||||
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
|
|
||||||
val assignArgument = AsmAssignment(
|
|
||||||
singleArg,
|
|
||||||
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
|
|
||||||
false, program.memsizer, target.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(
|
|
||||||
singleArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
|
|
||||||
false, program.memsizer, target.position
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// arg goes via parameter variable
|
|
||||||
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
|
|
||||||
AsmAssignment(
|
|
||||||
singleArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
|
|
||||||
false, program.memsizer, target.position
|
|
||||||
)
|
|
||||||
}
|
|
||||||
translateNormalAssignment(assignArgument)
|
|
||||||
}
|
|
||||||
if(targetStmt.shouldSaveX())
|
|
||||||
saveRegisterLocal(CpuRegister.X, scope)
|
|
||||||
out(" jsr ${asmSymbolName(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
|
||||||
if(parameter!=null) {
|
if(parameter!=null) {
|
||||||
val sub = parameter.definingSubroutine!!
|
val sub = parameter.definingSubroutine!!
|
||||||
require(sub.isAsmSubroutine) { "push/pop arg passing only supported on asmsubs" }
|
require(sub.isAsmSubroutine) { "push/pop arg passing only supported on asmsubs ${sub.position}" }
|
||||||
val shouldKeepA = sub.asmParameterRegisters.any { it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY }
|
val shouldKeepA = sub.asmParameterRegisters.any { it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY }
|
||||||
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
|
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
|
||||||
if(reg.statusflag!=null) {
|
if(reg.statusflag!=null) {
|
||||||
@ -3000,7 +3012,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)
|
||||||
@ -3014,20 +3026,33 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun popCpuStack(dt: DataType, targetAsmVarName: String) {
|
||||||
|
when(dt) {
|
||||||
|
in ByteDatatypes -> out(" pla | sta $targetAsmVarName")
|
||||||
|
in WordDatatypes -> out(" pla | sta $targetAsmVarName+1 | pla | sta $targetAsmVarName")
|
||||||
|
else -> throw AssemblyError("can't pop $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun pushCpuStack(dt: DataType, value: Expression) {
|
internal fun pushCpuStack(dt: DataType, value: Expression) {
|
||||||
val signed = value.inferType(program).oneOf(DataType.BYTE, DataType.WORD)
|
val signed = value.inferType(program).oneOf(DataType.BYTE, DataType.WORD)
|
||||||
if(dt in ByteDatatypes) {
|
if(dt in ByteDatatypes) {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.A, signed)
|
assignExpressionToRegister(value, RegisterOrPair.A, signed)
|
||||||
out(" pha")
|
out(" pha")
|
||||||
} else {
|
} else if(dt in WordDatatypes) {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.AY, signed)
|
assignExpressionToRegister(value, RegisterOrPair.AY, signed)
|
||||||
if (isTargetCpu(CpuType.CPU65c02))
|
if (isTargetCpu(CpuType.CPU65c02))
|
||||||
out(" pha | phy")
|
out(" pha | phy")
|
||||||
else
|
else
|
||||||
out(" pha | tya | pha")
|
out(" pha | tya | pha")
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("can't push $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
@ -44,7 +44,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
mods= optimizeJsrRts(linesByFour)
|
mods= optimizeJsrRtsAndOtherCombinations(linesByFour)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
@ -59,7 +59,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO more assembly optimizations
|
mods = optimizeSamePointerIndexing(linesByFourteen, machine, program)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO more assembly peephole optimizations
|
||||||
|
|
||||||
return numberOfOptimizations
|
return numberOfOptimizations
|
||||||
}
|
}
|
||||||
@ -320,6 +327,48 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||||
|
|
||||||
|
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||||
|
// if Y isn't modified in between we can omit the second LDY:
|
||||||
|
// ldy #0
|
||||||
|
// lda (ptr),y
|
||||||
|
// ora #3 ; <-- instruction(s) that don't modify Y
|
||||||
|
// ldy #0 ; <-- can be removed
|
||||||
|
// sta (ptr),y
|
||||||
|
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFourteen) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
val fourth = lines[3].value.trimStart()
|
||||||
|
val fifth = lines[4].value.trimStart()
|
||||||
|
val sixth = lines[5].value.trimStart()
|
||||||
|
|
||||||
|
if(first.startsWith("ldy") && second.startsWith("lda") && fourth.startsWith("ldy") && fifth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
val fifthvalue = fifth.substring(4)
|
||||||
|
if("y" !in third && firstvalue==fourthvalue && secondvalue==fifthvalue && secondvalue.endsWith(",y") && fifthvalue.endsWith(",y")) {
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(first.startsWith("ldy") && second.startsWith("lda") && fifth.startsWith("ldy") && sixth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val fifthvalue = fifth.substring(4)
|
||||||
|
val sixthvalue = sixth.substring(4)
|
||||||
|
if("y" !in third && "y" !in fourth && firstvalue==fifthvalue && secondvalue==sixthvalue && secondvalue.endsWith(",y") && sixthvalue.endsWith(",y")) {
|
||||||
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -401,7 +450,7 @@ private fun getAddressArg(line: String, program: Program): UInt? {
|
|||||||
val identMatch = identifierRegex.find(loadArg)
|
val identMatch = identifierRegex.find(loadArg)
|
||||||
if(identMatch!=null) {
|
if(identMatch!=null) {
|
||||||
val identifier = identMatch.value
|
val identifier = identMatch.value
|
||||||
val decl = program.toplevelModule.lookup(identifier.split(".")) as? VarDecl
|
val decl = program.toplevelModule.lookup(identifier.split('.')) as? VarDecl
|
||||||
if(decl!=null) {
|
if(decl!=null) {
|
||||||
when(decl.type){
|
when(decl.type){
|
||||||
VarDeclType.VAR -> null
|
VarDeclType.VAR -> null
|
||||||
@ -437,8 +486,11 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// jsr Sub + rts -> jmp Sub
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
// rts + jmp -> remove jmp
|
||||||
|
// rts + bxx -> remove bxx
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (lines in linesByFour) {
|
for (lines in linesByFour) {
|
||||||
val first = lines[0].value
|
val first = lines[0].value
|
||||||
@ -447,6 +499,28 @@ private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<
|
|||||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
mods += Modification(lines[1].index, true, null)
|
mods += Modification(lines[1].index, true, null)
|
||||||
}
|
}
|
||||||
|
else if (" rts" in first || "\trts" in first) {
|
||||||
|
if (" jmp" in second || "\tjmp" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bra" in second || "\tbra" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcc" in second || "\tbcc" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcs" in second || "\tbcs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" beq" in second || "\tbeq" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bne" in second || "\tbne" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bmi" in second || "\tbmi" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bpl" in second || "\tbpl" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvs" in second || "\tbvs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvc" in second || "\tbvc" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ 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 prog8.code.core.SourceCode
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -124,7 +124,7 @@ internal class AssemblyProgram(
|
|||||||
breakpoints.add("break \$" + match.groupValues[1])
|
breakpoints.add("break \$" + match.groupValues[1])
|
||||||
}
|
}
|
||||||
val num = breakpoints.size
|
val num = breakpoints.size
|
||||||
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
breakpoints.add(1, "; $num breakpoints have been defined")
|
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||||
breakpoints.add(2, "del")
|
breakpoints.add(2, "del")
|
||||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||||
|
@ -6,19 +6,16 @@ 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,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen,
|
||||||
private val assignAsmGen: AssignmentAsmGen,
|
private val assignAsmGen: AssignmentAsmGen) {
|
||||||
private val allocations: VariableAllocator) {
|
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
|
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
|
||||||
@ -30,43 +27,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 translateUnaryFunctioncall(name: String, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
|
||||||
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, argExpressions, program).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
|
||||||
@ -81,20 +41,8 @@ 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)
|
|
||||||
"min", "max" -> funcMinMax(fcall, func, resultToStack, resultRegister, sscope)
|
|
||||||
"sum" -> funcSum(fcall, resultToStack, resultRegister, sscope)
|
|
||||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"sin8", "sin8u", "sin16", "sin16u",
|
|
||||||
"sinr8", "sinr8u", "sinr16", "sinr16u",
|
|
||||||
"cos8", "cos8u", "cos16", "cos16u",
|
|
||||||
"cosr8", "cosr8u", "cosr16", "cosr16u" -> funcSinCosInt(fcall, func, resultToStack, resultRegister, sscope)
|
|
||||||
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"sin", "cos", "tan", "atan",
|
|
||||||
"ln", "log2", "sqrt", "rad",
|
|
||||||
"deg", "round", "floor", "ceil",
|
|
||||||
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
|
|
||||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
|
||||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
@ -129,7 +77,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall)
|
"callfar" -> funcCallFar(fcall)
|
||||||
"callrom" -> funcCallRom(fcall)
|
"callrom" -> funcCallRom(fcall)
|
||||||
"syscall", "syscall1", "syscall2", "syscall3" -> throw AssemblyError("6502 assembly target doesn't use syscall function interface")
|
|
||||||
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,20 +137,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) {
|
||||||
@ -213,7 +159,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)}""")
|
||||||
}
|
}
|
||||||
@ -221,7 +167,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()}""")
|
||||||
}
|
}
|
||||||
@ -308,15 +254,27 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
|
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
if(arg1.isSimple) {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
} else {
|
||||||
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
||||||
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
if(arg1.isSimple) {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
} else {
|
||||||
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
||||||
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -342,13 +300,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine)
|
if(arg1.isSimple) {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine)
|
||||||
asmgen.out("""
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
cpy P8ZP_SCRATCH_W1+1
|
asmgen.out("""
|
||||||
bne +
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
cmp P8ZP_SCRATCH_W1
|
bne +
|
||||||
+""")
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -360,25 +330,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
if(discardResult || fcall !is BuiltinFunctionCall)
|
if(discardResult || fcall !is BuiltinFunctionCall)
|
||||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||||
val name = (fcall.args[0] as StringLiteral).value
|
val name = (fcall.args[0] as StringLiteral).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||||
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
|
val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position)
|
||||||
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
|
|
||||||
|
|
||||||
val existing = allocations.getMemorySlab(name)
|
|
||||||
if(existing!=null && (existing.first!=size || existing.second!=align))
|
|
||||||
throw AssemblyError("memory slab '$name' already exists with a different size or alignment at ${fcall.position}")
|
|
||||||
|
|
||||||
val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position)
|
|
||||||
slabname.linkParents(fcall)
|
slabname.linkParents(fcall)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
@ -387,27 +349,10 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
|
||||||
translateArguments(fcall.args, func, scope)
|
|
||||||
if(resultToStack)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${func.name}_stack")
|
|
||||||
else
|
|
||||||
when(func.name) {
|
|
||||||
"sin8", "sin8u", "sinr8", "sinr8u", "cos8", "cos8u", "cosr8", "cosr8u" -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
|
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
|
||||||
}
|
|
||||||
"sin16", "sin16u", "sinr16", "sinr16u", "cos16", "cos16u", "cosr16", "cosr16u" -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcReverse(fcall: IFunctionCall) {
|
private fun funcReverse(fcall: IFunctionCall) {
|
||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
@ -539,6 +484,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)
|
||||||
@ -547,6 +493,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("""
|
||||||
@ -640,6 +587,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)
|
||||||
@ -648,6 +596,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("""
|
||||||
@ -682,18 +631,14 @@ 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) {
|
||||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) {
|
||||||
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
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)
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
} else {
|
||||||
translateArguments(fcall.args, func, scope)
|
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
if(resultToStack)
|
|
||||||
asmgen.out(" jsr floats.func_${func.name}_stack")
|
|
||||||
else {
|
|
||||||
asmgen.out(" jsr floats.func_${func.name}_fac1")
|
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
|
||||||
}
|
}
|
||||||
|
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
@ -717,7 +662,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,509 +678,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A | ldy #0")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
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 funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
|
||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
if(resultToStack) {
|
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
|
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
|
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
|
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_B -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_W -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
|
||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
if(resultToStack) {
|
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
|
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
|
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
|
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_stack")
|
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_stack")
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_B -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_W -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
asmgen.out(" jsr floats.func_sum_f_fac1")
|
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1244,49 +692,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)
|
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
DataType.UBYTE -> asmgen.out(" ldy #0")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_stack")
|
DataType.UWORD -> {}
|
||||||
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
DataType.UBYTE -> asmgen.out(" ldy #0")
|
||||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
DataType.UWORD -> {}
|
||||||
}
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
in WordDatatypes -> {
|
|
||||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out(" jsr floats.abs_f_fac1")
|
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
|
||||||
when(func.name) {
|
|
||||||
"rnd" -> {
|
|
||||||
if(resultToStack)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
|
||||||
else {
|
|
||||||
asmgen.out(" jsr math.randbyte")
|
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"rndw" -> {
|
|
||||||
if(resultToStack)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_rndw_stack")
|
|
||||||
else {
|
|
||||||
asmgen.out(" jsr math.randword")
|
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("wrong func")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1432,14 +852,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 -> {
|
||||||
@ -1596,8 +1016,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
private fun outputAddressAndLenghtOfArray(arg: Expression) {
|
private fun outputAddressAndLenghtOfArray(arg: Expression) {
|
||||||
// address in P8ZP_SCRATCH_W1, number of elements in A
|
// address in P8ZP_SCRATCH_W1, number of elements in A
|
||||||
arg as IdentifierReference
|
arg as IdentifierReference
|
||||||
|
val arrayVar = arg.targetVarDecl(program)!!
|
||||||
|
if(!arrayVar.isArray)
|
||||||
|
throw AssemblyError("length of non-array requested")
|
||||||
|
val size = arrayVar.arraysize!!.constIndex()!!
|
||||||
val identifierName = asmgen.asmVariableName(arg)
|
val identifierName = asmgen.asmVariableName(arg)
|
||||||
val size = arg.targetVarDecl(program)!!.arraysize!!.constIndex()!!
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$identifierName
|
lda #<$identifierName
|
||||||
ldy #>$identifierName
|
ldy #>$identifierName
|
||||||
@ -1653,7 +1076,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)
|
||||||
}
|
}
|
||||||
@ -1669,7 +1092,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,22 +49,11 @@ 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)
|
||||||
|
|
||||||
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
|
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter
|
||||||
// Output only the code to set up the parameters and perform the actual call
|
// Output only the code to set up the parameters and perform the actual call
|
||||||
// NOTE: does NOT output the code to deal with the result values!
|
// NOTE: does NOT output the code to deal with the result values!
|
||||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||||
@ -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 {
|
||||||
@ -148,7 +124,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
// this is called when one or more of the arguments are 'complex' and
|
// this is called when one or more of the arguments are 'complex' and
|
||||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||||
|
|
||||||
require(callee.isAsmSubroutine)
|
require(callee.isAsmSubroutine) { "register args only for asm subroutine ${callee.position}" }
|
||||||
if(callee.parameters.isEmpty())
|
if(callee.parameters.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -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) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.escape
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.code.*
|
import prog8.code.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
@ -45,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()
|
||||||
}
|
}
|
||||||
@ -71,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 -> {
|
||||||
@ -128,9 +145,24 @@ 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 | rts")
|
asmgen.out(" jsr main.start")
|
||||||
|
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||||
|
}
|
||||||
|
"c64" -> {
|
||||||
|
asmgen.out(" jsr main.start | lda #31 | sta $01")
|
||||||
|
if(!options.noSysInit)
|
||||||
|
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||||
|
else
|
||||||
|
asmgen.out(" rts")
|
||||||
|
}
|
||||||
|
"c128" -> {
|
||||||
|
asmgen.out(" jsr main.start")
|
||||||
|
// TODO c128: how to bank basic+kernal back in?
|
||||||
|
if(!options.noSysInit)
|
||||||
|
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||||
|
else
|
||||||
|
asmgen.out(" rts")
|
||||||
}
|
}
|
||||||
"c64" -> asmgen.out(" jsr main.start | lda #31 | sta $01 | rts")
|
|
||||||
else -> asmgen.jmp("main.start")
|
else -> asmgen.jmp("main.start")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,23 +170,15 @@ internal class ProgramAndVarsGen(
|
|||||||
private fun memorySlabs() {
|
private fun memorySlabs() {
|
||||||
asmgen.out("; memory slabs")
|
asmgen.out("; memory slabs")
|
||||||
asmgen.out("prog8_slabs\t.block")
|
asmgen.out("prog8_slabs\t.block")
|
||||||
for((name, info) in allocator.memorySlabs) {
|
for(slab in symboltable.allMemorySlabs) {
|
||||||
if(info.second>1u)
|
if(slab.align>1u)
|
||||||
asmgen.out("\t.align ${info.second.toHex()}")
|
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||||
asmgen.out("$name\t.fill ${info.first}")
|
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
||||||
}
|
}
|
||||||
asmgen.out("\t.bend")
|
asmgen.out("\t.bend")
|
||||||
}
|
}
|
||||||
|
|
||||||
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()")
|
||||||
}
|
}
|
||||||
@ -178,23 +202,17 @@ internal class ProgramAndVarsGen(
|
|||||||
asmsubs2asm(block.statements)
|
asmsubs2asm(block.statements)
|
||||||
|
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
asmgen.out("; subroutines in this block")
|
|
||||||
|
|
||||||
// First translate regular statements, and then put the subroutines at the end.
|
|
||||||
// (regular statements = everything except the initialization assignments;
|
|
||||||
// these will be part of the prog8_init_vars init routine generated below)
|
|
||||||
val initializers = blockVariableInitializers.getValue(block)
|
val initializers = blockVariableInitializers.getValue(block)
|
||||||
val statements = block.statements.filterNot { it in initializers }
|
val notInitializers = block.statements.filterNot { it in initializers }
|
||||||
val (subroutine, stmts) = statements.partition { it is Subroutine }
|
notInitializers.forEach { asmgen.translate(it) }
|
||||||
stmts.forEach { asmgen.translate(it) }
|
|
||||||
subroutine.forEach { asmgen.translate(it) }
|
|
||||||
|
|
||||||
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +223,7 @@ internal class ProgramAndVarsGen(
|
|||||||
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
||||||
|
|
||||||
private fun createBlockVariables(block: Block) {
|
private fun createBlockVariables(block: Block) {
|
||||||
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
|
val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
|
||||||
require(scope.type==StNodeType.BLOCK)
|
require(scope.type==StNodeType.BLOCK)
|
||||||
val varsInBlock = getVars(scope)
|
val varsInBlock = getVars(scope)
|
||||||
|
|
||||||
@ -224,7 +242,7 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInBlock
|
val variables = varsInBlock
|
||||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) }
|
||||||
.map { it.value as StStaticVariable }
|
.map { it.value as StStaticVariable }
|
||||||
nonZpVariables2asm(variables)
|
nonZpVariables2asm(variables)
|
||||||
}
|
}
|
||||||
@ -246,19 +264,29 @@ 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.joinToString(".")) { throw AssemblyError("lookup") }
|
||||||
require(scope.type==StNodeType.SUBROUTINE)
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
val varsInSubroutine = getVars(scope)
|
val varsInSubroutine = getVars(scope)
|
||||||
|
|
||||||
@ -285,7 +313,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
|
||||||
@ -293,8 +321,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)
|
||||||
}
|
}
|
||||||
@ -329,11 +357,11 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) }
|
||||||
.map { it.value as StStaticVariable }
|
.map { it.value as StStaticVariable }
|
||||||
nonZpVariables2asm(variables)
|
nonZpVariables2asm(variables)
|
||||||
|
|
||||||
asmgen.out(" .pend\n")
|
asmgen.out(" $asmEndScope\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,14 +428,14 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ZpStringWithInitial(
|
private class ZpStringWithInitial(
|
||||||
val name: List<String>,
|
val name: String,
|
||||||
val alloc: Zeropage.ZpAllocation,
|
val alloc: MemoryAllocator.VarAllocation,
|
||||||
val value: Pair<String, Encoding>
|
val value: Pair<String, Encoding>
|
||||||
)
|
)
|
||||||
|
|
||||||
private class ZpArrayWithInitial(
|
private class ZpArrayWithInitial(
|
||||||
val name: List<String>,
|
val name: String,
|
||||||
val alloc: Zeropage.ZpAllocation,
|
val alloc: MemoryAllocator.VarAllocation,
|
||||||
val value: StArray
|
val value: StArray
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -415,9 +443,10 @@ 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 scopedName = variable.key.joinToString(".")
|
||||||
if(svar.initialStringValue!=null)
|
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||||
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!))
|
if(svar.onetimeInitializationStringValue!=null)
|
||||||
|
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -426,15 +455,17 @@ 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 scopedName = variable.key.joinToString(".")
|
||||||
if(svar.initialArrayValue!=null)
|
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||||
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!))
|
if(svar.onetimeInitializationArrayValue!=null)
|
||||||
|
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun zeropagevars2asm(varNames: Set<List<String>>) {
|
private fun zeropagevars2asm(varNames: Set<String>) {
|
||||||
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }
|
val namesLists = varNames.map { it.split('.') }.toSet()
|
||||||
|
val zpVariables = allocator.zeropageVars.filter { it.key in namesLists }.toList().sortedBy { it.second.address }
|
||||||
for ((scopedName, zpvar) in zpVariables) {
|
for ((scopedName, zpvar) in zpVariables) {
|
||||||
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
|
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
|
||||||
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
||||||
@ -445,9 +476,9 @@ internal class ProgramAndVarsGen(
|
|||||||
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
asmgen.out("; non-zeropage variables")
|
asmgen.out("; non-zeropage variables")
|
||||||
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
|
val (stringvars, othervars) = variables.sortedBy { it.name }.partition { it.dt==DataType.STR }
|
||||||
stringvars.forEach {
|
stringvars.forEach {
|
||||||
outputStringvar(it.name, it.initialStringValue!!.second, it.initialStringValue!!.first)
|
outputStringvar(it.name, it.onetimeInitializationStringValue!!.second, it.onetimeInitializationStringValue!!.first)
|
||||||
}
|
}
|
||||||
othervars.sortedBy { it.type }.forEach {
|
othervars.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
@ -457,11 +488,11 @@ internal class ProgramAndVarsGen(
|
|||||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||||
val name = variable.name
|
val name = variable.name
|
||||||
val initialValue: Number =
|
val initialValue: Number =
|
||||||
if(variable.initialNumericValue!=null) {
|
if(variable.onetimeInitializationNumericValue!=null) {
|
||||||
if(variable.dt== DataType.FLOAT)
|
if(variable.dt== DataType.FLOAT)
|
||||||
variable.initialNumericValue!!
|
variable.onetimeInitializationNumericValue!!
|
||||||
else
|
else
|
||||||
variable.initialNumericValue!!.toInt()
|
variable.onetimeInitializationNumericValue!!.toInt()
|
||||||
} else 0
|
} else 0
|
||||||
|
|
||||||
when (variable.dt) {
|
when (variable.dt) {
|
||||||
@ -473,14 +504,14 @@ 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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
throw AssemblyError("all string vars should have been interned into prog")
|
throw AssemblyError("all string vars should have been interned into prog")
|
||||||
}
|
}
|
||||||
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.length)
|
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
|
||||||
else -> {
|
else -> {
|
||||||
throw AssemblyError("weird dt")
|
throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -532,7 +563,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))
|
||||||
@ -551,10 +582,10 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
|
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
|
||||||
memvars.forEach {
|
memvars.sortedBy { it.address }.forEach {
|
||||||
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
||||||
}
|
}
|
||||||
consts.forEach {
|
consts.sortedBy { it.name }.forEach {
|
||||||
if(it.dt==DataType.FLOAT)
|
if(it.dt==DataType.FLOAT)
|
||||||
asmgen.out(" ${it.name} = ${it.value}")
|
asmgen.out(" ${it.name} = ${it.value}")
|
||||||
else
|
else
|
||||||
@ -572,7 +603,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:\"${escape(value).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))
|
||||||
@ -592,8 +623,8 @@ internal class ProgramAndVarsGen(
|
|||||||
if(it.number!=null) {
|
if(it.number!=null) {
|
||||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
else if(it.addressOf!=null) {
|
else if(it.addressOfSymbol!=null) {
|
||||||
asmgen.asmSymbolName(it.addressOf!!)
|
asmgen.asmSymbolName(it.addressOfSymbol!!)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw AssemblyError("weird array elt")
|
throw AssemblyError("weird array elt")
|
||||||
|
@ -15,22 +15,14 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
private val zeropage = options.compTarget.machine.zeropage
|
private val zeropage = options.compTarget.machine.zeropage
|
||||||
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
|
|
||||||
internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal
|
|
||||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
internal val zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables
|
internal val zeropageVars: Map<List<String>, MemoryAllocator.VarAllocation> = zeropage.allocatedVariables
|
||||||
|
|
||||||
init {
|
init {
|
||||||
allocateZeropageVariables()
|
allocateZeropageVariables()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getMemorySlab(name: String) = memorySlabsInternal[name]
|
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars
|
||||||
|
|
||||||
internal fun allocateMemorySlab(name: String, size: UInt, align: UInt) {
|
|
||||||
memorySlabsInternal[name] = Pair(size, align)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
|
|
||||||
|
|
||||||
internal fun getFloatAsmConst(number: Double): String {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
val asmName = globalFloatConsts[number]
|
val asmName = globalFloatConsts[number]
|
||||||
@ -64,7 +56,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
|
|
||||||
varsRequiringZp.forEach { variable ->
|
varsRequiringZp.forEach { variable ->
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName,
|
variable.scopedName.split('.'),
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.position,
|
||||||
@ -83,7 +75,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
varsPreferringZp.forEach { variable ->
|
varsPreferringZp.forEach { variable ->
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName,
|
variable.scopedName.split('.'),
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.position,
|
||||||
@ -96,13 +88,14 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
// try to allocate any other interger variables into the zeropage until it is full.
|
// try to allocate any other interger variables into the zeropage until it is full.
|
||||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
for (variable in varsDontCare.sortedBy { it.scopedName.size }) {
|
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
||||||
|
for (variable in sortedList) {
|
||||||
if(variable.dt in IntegerDatatypes) {
|
if(variable.dt in IntegerDatatypes) {
|
||||||
if(zeropage.free.isEmpty()) {
|
if(zeropage.free.isEmpty()) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName,
|
variable.scopedName.split('.'),
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.position,
|
||||||
@ -116,9 +109,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> {
|
||||||
@ -132,6 +125,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
collect(st)
|
collect(st)
|
||||||
return vars
|
return vars.sortedBy { it.dt }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
@ -211,7 +207,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
||||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||||
"source dt size must be less or equal to target dt size at $position"
|
"source dt size must be less or equal to target dt size at $position"
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,19 +16,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
) {
|
) {
|
||||||
fun translate(assign: AsmAssignment) {
|
fun translate(assign: AsmAssignment) {
|
||||||
require(assign.isAugmentable)
|
require(assign.isAugmentable)
|
||||||
require(assign.source.kind== SourceStorageKind.EXPRESSION)
|
require(assign.source.kind == SourceStorageKind.EXPRESSION) {
|
||||||
|
"non-expression assign value should be handled elsewhere ${assign.position}"
|
||||||
|
}
|
||||||
|
|
||||||
when (val value = assign.source.expression!!) {
|
when (val value = assign.source.expression!!) {
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
// A = -A , A = +A, A = ~A, A = not A
|
// A = -A , A = +A, A = ~A, A = not A
|
||||||
val target = assignmentAsmGen.virtualRegsToVariables(assign.target)
|
|
||||||
val itype = value.inferType(program)
|
|
||||||
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
when (value.operator) {
|
when (value.operator) {
|
||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> inplaceNegate(target, type)
|
"-" -> inplaceNegate(assign, false)
|
||||||
"~" -> inplaceInvert(target, type)
|
"~" -> inplaceInvert(assign)
|
||||||
"not" -> inplaceBooleanNot(target, type)
|
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,7 +235,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 +304,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 +315,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 +326,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 +386,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 +423,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 +480,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 +558,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 +644,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 +735,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 +780,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 +823,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 +837,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 +1054,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 +1091,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 +1099,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 +1263,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 +1272,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 +1344,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 +1516,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 +1526,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 +1562,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,138 +1795,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceInvert(assign: AsmAssignment) {
|
||||||
when (dt) {
|
val target = assign.target
|
||||||
DataType.UBYTE -> {
|
when (assign.target.datatype) {
|
||||||
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) {
|
|
||||||
when (dt) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -1974,7 +1840,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
||||||
else -> throw AssemblyError("no asm gen for in-place invert ubyte for ${target.kind}")
|
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1998,15 +1865,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
||||||
else -> throw AssemblyError("no asm gen for in-place invert uword for ${target.kind}")
|
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
else -> throw AssemblyError("invert of invalid type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun inplaceNegate(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
|
||||||
when (dt) {
|
val target = assign.target
|
||||||
|
val datatype = if(ignoreDatatype) {
|
||||||
|
when(target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> DataType.BYTE
|
||||||
|
DataType.UWORD, DataType.WORD -> DataType.WORD
|
||||||
|
else -> target.datatype
|
||||||
|
}
|
||||||
|
} else target.datatype
|
||||||
|
when (datatype) {
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -2030,9 +1906,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("invalid reg dt for byte negate")
|
else -> throw AssemblyError("invalid reg dt for byte negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't in-place negate")
|
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
||||||
else -> throw AssemblyError("no asm gen for in-place negate byte")
|
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -2089,12 +1966,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("invalid reg dt for word neg")
|
else -> throw AssemblyError("invalid reg dt for word neg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
|
||||||
else -> throw AssemblyError("no asm gen for in-place negate word")
|
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP")
|
||||||
|
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF")
|
||||||
|
else -> throw AssemblyError("invalid float register")
|
||||||
|
}
|
||||||
|
}
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
// simply flip the sign bit in the float
|
// simply flip the sign bit in the float
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -2104,10 +1990,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
||||||
else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}")
|
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
||||||
|
else -> throw AssemblyError("weird target for in-place float negation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("negate of invalid type $dt")
|
else -> throw AssemblyError("negate of invalid type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,12 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':codeAst')
|
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
|
implementation project(':intermediate')
|
||||||
|
implementation project(':codeGenIntermediate')
|
||||||
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"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
<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="module" module-name="codeAst" />
|
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||||
|
<orderEntry type="module" module-name="intermediate" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,39 +0,0 @@
|
|||||||
package prog8.codegen.experimental
|
|
||||||
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.ast.PtProgram
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.IAssemblyGenerator
|
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
NOTE: The goal is to keep the dependencies as lean as possible! For now, we depend only on:
|
|
||||||
- codeAst (the 'lean' new AST and the SymbolTable)
|
|
||||||
- codeCore (various base enums and interfaces)
|
|
||||||
|
|
||||||
This *should* be enough to build a complete code generator with. But we'll see :)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class AsmGen(internal val program: PtProgram,
|
|
||||||
internal val symbolTable: SymbolTable,
|
|
||||||
internal val options: CompilationOptions,
|
|
||||||
internal val errors: IErrorReporter
|
|
||||||
): IAssemblyGenerator {
|
|
||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
|
||||||
|
|
||||||
println("\n** experimental code generator **\n")
|
|
||||||
|
|
||||||
println("Writing AST into XML form...")
|
|
||||||
val xmlConv = AstToXmlConverter(program, symbolTable, options)
|
|
||||||
xmlConv.writeXml()
|
|
||||||
|
|
||||||
println("..todo: create assembly program into ${options.outputDir.toAbsolutePath()}..")
|
|
||||||
|
|
||||||
return AssemblyProgram("dummy")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package prog8.codegen.experimental
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyProgram(override val name: String) : IAssemblyProgram
|
|
||||||
{
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
|
||||||
println("..todo: assemble code into binary..")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,667 +0,0 @@
|
|||||||
package prog8.codegen.experimental
|
|
||||||
|
|
||||||
import prog8.code.*
|
|
||||||
import prog8.code.ast.*
|
|
||||||
import prog8.code.core.*
|
|
||||||
import javax.xml.stream.XMLOutputFactory
|
|
||||||
import kotlin.io.path.Path
|
|
||||||
import kotlin.io.path.absolutePathString
|
|
||||||
import kotlin.io.path.div
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
NOTE: The goal is to keep the dependencies as lean as possible! For now, we depend only on:
|
|
||||||
- codeAst (the 'lean' new AST and the SymbolTable)
|
|
||||||
- codeCore (various base enums and interfaces)
|
|
||||||
|
|
||||||
This *should* be enough to build a complete code generator with. But we'll see :)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class AstToXmlConverter(internal val program: PtProgram,
|
|
||||||
internal val symbolTable: SymbolTable,
|
|
||||||
internal val options: CompilationOptions
|
|
||||||
) {
|
|
||||||
|
|
||||||
private lateinit var xml: IndentingXmlWriter
|
|
||||||
|
|
||||||
fun writeXml() {
|
|
||||||
val writer = (options.outputDir / Path(program.name+"-ast.xml")).toFile().printWriter()
|
|
||||||
xml = IndentingXmlWriter(XMLOutputFactory.newFactory().createXMLStreamWriter(writer))
|
|
||||||
xml.doc()
|
|
||||||
xml.elt("program")
|
|
||||||
xml.attr("name", program.name)
|
|
||||||
xml.startChildren()
|
|
||||||
writeOptions(options)
|
|
||||||
program.children.forEach { writeNode(it) }
|
|
||||||
writeSymboltable(symbolTable)
|
|
||||||
xml.endElt()
|
|
||||||
xml.endDoc()
|
|
||||||
xml.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeSymboltable(st: SymbolTable) {
|
|
||||||
xml.elt("symboltable")
|
|
||||||
xml.startChildren()
|
|
||||||
st.flat.forEach{ (name, entry) ->
|
|
||||||
xml.elt("entry")
|
|
||||||
xml.attr("name", name.joinToString("."))
|
|
||||||
xml.attr("type", entry.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
writeStNode(entry)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeStNode(node: StNode) {
|
|
||||||
when(node.type) {
|
|
||||||
StNodeType.GLOBAL,
|
|
||||||
StNodeType.LABEL,
|
|
||||||
StNodeType.BLOCK,
|
|
||||||
StNodeType.BUILTINFUNC,
|
|
||||||
StNodeType.SUBROUTINE -> {/* no additional info*/}
|
|
||||||
StNodeType.ROMSUB -> {
|
|
||||||
node as StRomSub
|
|
||||||
xml.elt("romsub")
|
|
||||||
xml.attr("address", node.address.toString())
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
StNodeType.STATICVAR -> {
|
|
||||||
node as StStaticVariable
|
|
||||||
xml.elt("var")
|
|
||||||
xml.attr("type", node.dt.name)
|
|
||||||
xml.attr("zpwish", node.zpwish.name)
|
|
||||||
if(node.length!=null)
|
|
||||||
xml.attr("length", node.length.toString())
|
|
||||||
if(node.initialNumericValue!=null || node.initialArrayValue!=null || node.initialStringValue!=null) {
|
|
||||||
xml.startChildren()
|
|
||||||
if(node.initialNumericValue!=null) {
|
|
||||||
writeNumber(node.dt, node.initialNumericValue!!)
|
|
||||||
}
|
|
||||||
if(node.initialStringValue!=null) {
|
|
||||||
xml.writeTextNode(
|
|
||||||
"string",
|
|
||||||
listOf(Pair("encoding", node.initialStringValue!!.second.name)),
|
|
||||||
node.initialStringValue!!.first,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if(node.initialArrayValue!=null) {
|
|
||||||
xml.elt("array")
|
|
||||||
xml.startChildren()
|
|
||||||
val eltDt = ArrayToElementTypes.getValue(node.dt)
|
|
||||||
node.initialArrayValue!!.forEach {
|
|
||||||
if(it.number!=null) {
|
|
||||||
writeNumber(eltDt, it.number!!)
|
|
||||||
}
|
|
||||||
if(it.addressOf!=null) {
|
|
||||||
xml.elt("addressof")
|
|
||||||
xml.attr("symbol", it.addressOf!!.joinToString("."))
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
StNodeType.MEMVAR -> {
|
|
||||||
node as StMemVar
|
|
||||||
xml.writeTextNode("memvar",
|
|
||||||
listOf(Pair("type", node.dt.name)),
|
|
||||||
node.address.toString(),
|
|
||||||
false)
|
|
||||||
}
|
|
||||||
StNodeType.CONSTANT -> {
|
|
||||||
node as StConstant
|
|
||||||
xml.writeTextNode("const",
|
|
||||||
listOf(Pair("type", node.dt.name)),
|
|
||||||
intOrDouble(node.dt, node.value).toString(),
|
|
||||||
false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeOptions(options: CompilationOptions) {
|
|
||||||
xml.elt("options")
|
|
||||||
xml.attr("target", options.compTarget.name)
|
|
||||||
xml.attr("output", options.output.name)
|
|
||||||
xml.attr("launcher", options.launcher.name)
|
|
||||||
xml.attr("zeropage", options.zeropage.name)
|
|
||||||
xml.attr("loadaddress", options.loadAddress.toString())
|
|
||||||
xml.attr("floatsenabled", options.floats.toString())
|
|
||||||
xml.attr("nosysinit", options.noSysInit.toString())
|
|
||||||
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
|
|
||||||
xml.attr("optimize", options.optimize.toString())
|
|
||||||
if(options.zpReserved.isNotEmpty()) {
|
|
||||||
xml.startChildren()
|
|
||||||
options.zpReserved.forEach {
|
|
||||||
xml.elt("zpreserved")
|
|
||||||
xml.attr("from", it.first.toString())
|
|
||||||
xml.attr("to", it.last.toString())
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeNode(it: PtNode) {
|
|
||||||
when(it) {
|
|
||||||
is PtBlock -> write(it)
|
|
||||||
is PtSub -> write(it)
|
|
||||||
is PtVariable -> write(it)
|
|
||||||
is PtAssignment -> write(it)
|
|
||||||
is PtConstant -> write(it)
|
|
||||||
is PtAsmSub -> write(it)
|
|
||||||
is PtAddressOf -> write(it)
|
|
||||||
is PtArrayIndexer -> write(it)
|
|
||||||
is PtArray -> write(it)
|
|
||||||
is PtBinaryExpression -> write(it)
|
|
||||||
is PtBuiltinFunctionCall -> write(it)
|
|
||||||
is PtConditionalBranch -> write(it)
|
|
||||||
is PtContainmentCheck -> write(it)
|
|
||||||
is PtForLoop -> write(it)
|
|
||||||
is PtFunctionCall -> write(it)
|
|
||||||
is PtIdentifier -> write(it)
|
|
||||||
is PtIfElse -> write(it)
|
|
||||||
is PtInlineAssembly -> write(it)
|
|
||||||
is PtIncludeBinary -> write(it)
|
|
||||||
is PtJump -> write(it)
|
|
||||||
is PtMemoryByte -> write(it)
|
|
||||||
is PtMemMapped -> write(it)
|
|
||||||
is PtNumber -> write(it)
|
|
||||||
is PtPipe -> write(it)
|
|
||||||
is PtPostIncrDecr -> write(it)
|
|
||||||
is PtPrefix -> write(it)
|
|
||||||
is PtRange -> write(it)
|
|
||||||
is PtRepeatLoop -> write(it)
|
|
||||||
is PtReturn -> write(it)
|
|
||||||
is PtString -> write(it)
|
|
||||||
is PtTypeCast -> write(it)
|
|
||||||
is PtWhen -> write(it)
|
|
||||||
is PtWhenChoice -> write(it)
|
|
||||||
is PtLabel -> write(it)
|
|
||||||
is PtNop -> {}
|
|
||||||
is PtBreakpoint -> write(it)
|
|
||||||
is PtScopeVarsDecls -> write(it)
|
|
||||||
is PtNodeGroup -> it.children.forEach { writeNode(it) }
|
|
||||||
else -> TODO("$it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(vars: PtScopeVarsDecls) {
|
|
||||||
xml.elt("vars")
|
|
||||||
xml.startChildren()
|
|
||||||
vars.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(breakPt: PtBreakpoint) {
|
|
||||||
xml.elt("breakpoint")
|
|
||||||
xml.pos(breakPt.position)
|
|
||||||
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) {
|
|
||||||
xml.elt("array")
|
|
||||||
xml.attr("type", array.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
array.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(prefix: PtPrefix) {
|
|
||||||
xml.elt("prefix")
|
|
||||||
xml.attr("op", prefix.operator)
|
|
||||||
xml.attr("type", prefix.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("value")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(prefix.value)
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(string: PtString) =
|
|
||||||
xml.writeTextNode("string", listOf(Pair("encoding", string.encoding.name)), string.value, false)
|
|
||||||
|
|
||||||
private fun write(rept: PtRepeatLoop) {
|
|
||||||
xml.elt("repeat")
|
|
||||||
xml.pos(rept.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("count")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(rept.count)
|
|
||||||
xml.endElt()
|
|
||||||
writeNode(rept.statements)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(branch: PtConditionalBranch) {
|
|
||||||
xml.elt("conditionalbranch")
|
|
||||||
xml.attr("condition", branch.condition.name)
|
|
||||||
xml.pos(branch.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("true")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(branch.trueScope)
|
|
||||||
xml.endElt()
|
|
||||||
if(branch.falseScope.children.isNotEmpty()) {
|
|
||||||
xml.elt("false")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(branch.falseScope)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(check: PtContainmentCheck) {
|
|
||||||
xml.elt("containment")
|
|
||||||
xml.attr("type", check.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("element")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(check.children[0])
|
|
||||||
xml.endElt()
|
|
||||||
xml.elt("iterable")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(check.children[1])
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(range: PtRange) {
|
|
||||||
xml.elt("range")
|
|
||||||
xml.attr("type", range.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("from")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(range.from)
|
|
||||||
xml.endElt()
|
|
||||||
xml.elt("to")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(range.to)
|
|
||||||
xml.endElt()
|
|
||||||
xml.elt("step")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(range.step)
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(forLoop: PtForLoop) {
|
|
||||||
xml.elt("for")
|
|
||||||
xml.attr("loopvar", strTargetName(forLoop.variable))
|
|
||||||
xml.pos(forLoop.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("iterable")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(forLoop.iterable)
|
|
||||||
xml.endElt()
|
|
||||||
writeNode(forLoop.statements)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(membyte: PtMemoryByte) {
|
|
||||||
xml.elt("membyte")
|
|
||||||
xml.attr("type", membyte.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("address")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(membyte.address)
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(whenStmt: PtWhen) {
|
|
||||||
xml.elt("when")
|
|
||||||
xml.pos(whenStmt.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("value")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(whenStmt.value)
|
|
||||||
xml.endElt()
|
|
||||||
xml.elt("choices")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(whenStmt.choices)
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(choice: PtWhenChoice) {
|
|
||||||
xml.elt("choice")
|
|
||||||
if(choice.isElse) {
|
|
||||||
xml.attr("else", "true")
|
|
||||||
xml.startChildren()
|
|
||||||
} else {
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("values")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(choice.values)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
writeNode(choice.statements)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(inlineAsm: PtInlineAssembly) {
|
|
||||||
xml.elt("assembly")
|
|
||||||
xml.pos(inlineAsm.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.writeTextNode("code", emptyList(), inlineAsm.assembly)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(inlineBinary: PtIncludeBinary) {
|
|
||||||
xml.elt("binary")
|
|
||||||
xml.attr("filename", inlineBinary.file.absolutePathString())
|
|
||||||
if(inlineBinary.offset!=null)
|
|
||||||
xml.attr("offset", inlineBinary.offset!!.toString())
|
|
||||||
if(inlineBinary.length!=null)
|
|
||||||
xml.attr("length", inlineBinary.length!!.toString())
|
|
||||||
xml.pos(inlineBinary.position)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(fcall: PtBuiltinFunctionCall) {
|
|
||||||
xml.elt("builtinfcall")
|
|
||||||
xml.attr("name", fcall.name)
|
|
||||||
if(fcall.void)
|
|
||||||
xml.attr("type", "VOID")
|
|
||||||
else
|
|
||||||
xml.attr("type", fcall.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
fcall.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(cast: PtTypeCast) {
|
|
||||||
xml.elt("cast")
|
|
||||||
xml.attr("type", cast.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(cast.value)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(aix: PtArrayIndexer) {
|
|
||||||
xml.elt("arrayindexed")
|
|
||||||
xml.attr("type", aix.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
write(aix.variable)
|
|
||||||
writeNode(aix.index)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(binexpr: PtBinaryExpression) {
|
|
||||||
xml.elt("binexpr")
|
|
||||||
xml.attr("op", binexpr.operator)
|
|
||||||
xml.attr("type", binexpr.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(binexpr.left)
|
|
||||||
writeNode(binexpr.right)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(addrof: PtAddressOf) {
|
|
||||||
xml.elt("addressof")
|
|
||||||
xml.attr("symbol", strTargetName(addrof.identifier))
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(fcall: PtFunctionCall) {
|
|
||||||
xml.elt("fcall")
|
|
||||||
xml.attr("name", strTargetName(fcall))
|
|
||||||
if(fcall.void)
|
|
||||||
xml.attr("type", "VOID")
|
|
||||||
else
|
|
||||||
xml.attr("type", fcall.type.name)
|
|
||||||
xml.pos(fcall.position)
|
|
||||||
xml.startChildren()
|
|
||||||
fcall.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(number: PtNumber) = writeNumber(number.type, number.number)
|
|
||||||
|
|
||||||
private fun writeNumber(type: DataType, number: Double) =
|
|
||||||
xml.writeTextNode("number", listOf(Pair("type", type.name)), intOrDouble(type, number).toString(), false)
|
|
||||||
|
|
||||||
private fun write(symbol: PtIdentifier) {
|
|
||||||
xml.elt("symbol")
|
|
||||||
xml.attr("name", strTargetName(symbol))
|
|
||||||
xml.attr("type", symbol.type.name)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(assign: PtAssignment) {
|
|
||||||
xml.elt("assign")
|
|
||||||
xml.attr("aug", assign.augmentable.toString())
|
|
||||||
xml.pos(assign.position)
|
|
||||||
xml.startChildren()
|
|
||||||
write(assign.target)
|
|
||||||
writeNode(assign.value)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(ifElse: PtIfElse) {
|
|
||||||
xml.elt("ifelse")
|
|
||||||
xml.pos(ifElse.position)
|
|
||||||
xml.startChildren()
|
|
||||||
xml.elt("condition")
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(ifElse.condition)
|
|
||||||
xml.endElt()
|
|
||||||
xml.elt("true")
|
|
||||||
xml.pos(ifElse.ifScope.position)
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(ifElse.ifScope)
|
|
||||||
xml.endElt()
|
|
||||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
|
||||||
xml.elt("false")
|
|
||||||
xml.pos(ifElse.elseScope.position)
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(ifElse.elseScope)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(ret: PtReturn) {
|
|
||||||
xml.elt("return")
|
|
||||||
if(ret.hasValue) {
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(ret.value!!)
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(incdec: PtPostIncrDecr) {
|
|
||||||
if(incdec.operator=="++") xml.elt("inc") else xml.elt("dec")
|
|
||||||
xml.startChildren()
|
|
||||||
write(incdec.target)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(label: PtLabel) {
|
|
||||||
xml.elt("label")
|
|
||||||
xml.attr("name", label.scopedName.joinToString("."))
|
|
||||||
xml.pos(label.position)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(block: PtBlock) {
|
|
||||||
xml.elt("block")
|
|
||||||
xml.attr("name", block.scopedName.joinToString("."))
|
|
||||||
if(block.address!=null)
|
|
||||||
xml.attr("address", block.address!!.toString())
|
|
||||||
xml.attr("library", block.library.toString())
|
|
||||||
xml.pos(block.position)
|
|
||||||
xml.startChildren()
|
|
||||||
block.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(memMapped: PtMemMapped) {
|
|
||||||
xml.writeTextNode("memvar",
|
|
||||||
listOf(
|
|
||||||
Pair("name", memMapped.scopedName.joinToString(".")),
|
|
||||||
Pair("type", memMapped.type.name)
|
|
||||||
),
|
|
||||||
memMapped.address.toString(),
|
|
||||||
false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(target: PtAssignTarget) {
|
|
||||||
xml.elt("target")
|
|
||||||
xml.startChildren()
|
|
||||||
if(target.identifier!=null) {
|
|
||||||
writeNode(target.identifier!!)
|
|
||||||
} else if(target.memory!=null) {
|
|
||||||
writeNode(target.memory!!)
|
|
||||||
} else if(target.array!=null) {
|
|
||||||
writeNode(target.array!!)
|
|
||||||
} else
|
|
||||||
throw InternalCompilerException("weird assign target")
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(jump: PtJump) {
|
|
||||||
xml.elt("jump")
|
|
||||||
if(jump.identifier!=null) xml.attr("symbol", strTargetName(jump.identifier!!))
|
|
||||||
else if(jump.address!=null) xml.attr("address", jump.address!!.toString())
|
|
||||||
else if(jump.generatedLabel!=null) xml.attr("label", jump.generatedLabel!!)
|
|
||||||
else
|
|
||||||
throw InternalCompilerException("weird jump target")
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(sub: PtSub) {
|
|
||||||
xml.elt("sub")
|
|
||||||
xml.attr("name", sub.scopedName.joinToString("."))
|
|
||||||
if(sub.inline)
|
|
||||||
xml.attr("inline", "true")
|
|
||||||
xml.attr("returntype", sub.returntype?.toString() ?: "VOID")
|
|
||||||
xml.pos(sub.position)
|
|
||||||
xml.startChildren()
|
|
||||||
if(sub.parameters.isNotEmpty()) {
|
|
||||||
xml.elt("parameters")
|
|
||||||
xml.startChildren()
|
|
||||||
sub.parameters.forEach { write(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
sub.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(parameter: PtSubroutineParameter, registerOrStatusflag: RegisterOrStatusflag? = null) {
|
|
||||||
xml.elt("param")
|
|
||||||
xml.attr("name", parameter.name)
|
|
||||||
xml.attr("type", parameter.type.name)
|
|
||||||
if(registerOrStatusflag?.statusflag!=null) {
|
|
||||||
xml.attr("statusflag", registerOrStatusflag.statusflag!!.toString())
|
|
||||||
}
|
|
||||||
if(registerOrStatusflag?.registerOrPair!=null){
|
|
||||||
xml.attr("registers", registerOrStatusflag.registerOrPair!!.name)
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(asmSub: PtAsmSub) {
|
|
||||||
if(asmSub.address!=null) {
|
|
||||||
xml.elt("romsub")
|
|
||||||
xml.attr("name", asmSub.scopedName.joinToString("."))
|
|
||||||
xml.attr("address", asmSub.address!!.toString())
|
|
||||||
if(asmSub.inline)
|
|
||||||
xml.attr("inline", "true")
|
|
||||||
xml.pos(asmSub.position)
|
|
||||||
xml.startChildren()
|
|
||||||
paramsEtcetera(asmSub)
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
xml.elt("asmsub")
|
|
||||||
xml.attr("name", asmSub.scopedName.joinToString("."))
|
|
||||||
if(asmSub.inline)
|
|
||||||
xml.attr("inline", "true")
|
|
||||||
xml.pos(asmSub.position)
|
|
||||||
xml.startChildren()
|
|
||||||
paramsEtcetera(asmSub)
|
|
||||||
xml.elt("code")
|
|
||||||
xml.startChildren()
|
|
||||||
asmSub.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun paramsEtcetera(asmSub: PtAsmSub) {
|
|
||||||
if(asmSub.parameters.isNotEmpty()) {
|
|
||||||
xml.elt("parameters")
|
|
||||||
xml.startChildren()
|
|
||||||
asmSub.parameters.forEach { (param, reg) -> write(param, reg) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
if(asmSub.clobbers.isNotEmpty()) {
|
|
||||||
xml.elt("clobbers")
|
|
||||||
xml.attr("registers", asmSub.clobbers.map {it.name}.joinToString(","))
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
if(asmSub.retvalRegisters.isNotEmpty()) {
|
|
||||||
xml.elt("returns")
|
|
||||||
xml.startChildren()
|
|
||||||
asmSub.retvalRegisters.forEach {
|
|
||||||
xml.elt("register")
|
|
||||||
if(it.statusflag!=null)
|
|
||||||
xml.attr("statusflag", it.statusflag!!.toString())
|
|
||||||
if(it.registerOrPair!=null)
|
|
||||||
xml.attr("registers", it.registerOrPair!!.toString())
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(constant: PtConstant) {
|
|
||||||
xml.writeTextNode("const",
|
|
||||||
listOf(
|
|
||||||
Pair("name", constant.scopedName.joinToString(".")),
|
|
||||||
Pair("type", constant.type.name)
|
|
||||||
),
|
|
||||||
intOrDouble(constant.type, constant.value).toString(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(variable: PtVariable) {
|
|
||||||
// the variable declaration nodes are still present in the Ast,
|
|
||||||
// but the Symboltable should be used look up their details.
|
|
||||||
xml.elt("vardecl")
|
|
||||||
xml.attr("name", variable.scopedName.joinToString("."))
|
|
||||||
xml.attr("type", variable.type.name)
|
|
||||||
if(variable.arraySize!=null)
|
|
||||||
xml.attr("arraysize", variable.arraySize.toString())
|
|
||||||
if(variable.value!=null) {
|
|
||||||
// static initialization value
|
|
||||||
xml.startChildren()
|
|
||||||
writeNode(variable.value!!)
|
|
||||||
}
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun strTargetName(ident: PtIdentifier): String = ident.targetName.joinToString(".")
|
|
||||||
|
|
||||||
private fun strTargetName(call: PtFunctionCall): String = call.functionName.joinToString(".")
|
|
||||||
|
|
||||||
private fun intOrDouble(type: DataType, value: Double): Number =
|
|
||||||
if(type in IntegerDatatypes) value.toInt() else value
|
|
||||||
}
|
|
@ -0,0 +1,30 @@
|
|||||||
|
package prog8.codegen.experimental
|
||||||
|
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.IAssemblyGenerator
|
||||||
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
|
import prog8.intermediate.IRFileWriter
|
||||||
|
|
||||||
|
class CodeGen(private val program: PtProgram,
|
||||||
|
private val symbolTable: SymbolTable,
|
||||||
|
private val options: CompilationOptions,
|
||||||
|
private val errors: IErrorReporter
|
||||||
|
): IAssemblyGenerator {
|
||||||
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
|
|
||||||
|
// you could write a code generator directly on the PtProgram AST,
|
||||||
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
|
val irProgram = irCodeGen.generate()
|
||||||
|
|
||||||
|
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||||
|
IRFileWriter(irProgram, null).write()
|
||||||
|
|
||||||
|
println("** experimental codegen stub: no assembly generated **")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -1,89 +0,0 @@
|
|||||||
package prog8.codegen.experimental
|
|
||||||
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import java.util.*
|
|
||||||
import javax.xml.stream.XMLStreamWriter
|
|
||||||
|
|
||||||
class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
|
|
||||||
private var indent = 0
|
|
||||||
private var content = Stack<Boolean>()
|
|
||||||
|
|
||||||
fun doc(version: String? = null) = if(version==null) writeStartDocument() else writeStartDocument(version)
|
|
||||||
fun endDoc() = writeEndDocument()
|
|
||||||
fun elt(name: String) = writeStartElement(name)
|
|
||||||
fun attr(name: String, value: String) = writeAttribute(name, value)
|
|
||||||
fun attrs(attributes: List<Pair<String, String>>) = attributes.forEach { writeAttribute(it.first, it.second) }
|
|
||||||
fun startChildren() {
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
content.pop()
|
|
||||||
content.push(true)
|
|
||||||
}
|
|
||||||
fun endElt(writeIndent: Boolean=true) = writeEndElement(writeIndent)
|
|
||||||
fun pos(pos: Position) = writeAttribute("src", pos.toString())
|
|
||||||
fun comment(text: String) {
|
|
||||||
writeComment(text)
|
|
||||||
writeCharacters("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeStartDocument() {
|
|
||||||
xml.writeStartDocument()
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
content.push(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeStartDocument(version: String) {
|
|
||||||
xml.writeStartDocument(version)
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
content.push(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeEndDocument() {
|
|
||||||
xml.writeEndDocument()
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
require(indent==0)
|
|
||||||
require(content.size==1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeStartElement(name: String) {
|
|
||||||
xml.writeCharacters(" ".repeat(indent))
|
|
||||||
xml.writeStartElement(name)
|
|
||||||
indent++
|
|
||||||
content.push(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeStartElement(name: String, ns: String) {
|
|
||||||
xml.writeCharacters(" ".repeat(indent))
|
|
||||||
xml.writeStartElement(name, ns)
|
|
||||||
indent++
|
|
||||||
content.push(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeEndElement(writeIndents: Boolean) {
|
|
||||||
indent--
|
|
||||||
if(content.pop() && writeIndents)
|
|
||||||
xml.writeCharacters(" ".repeat(indent))
|
|
||||||
xml.writeEndElement()
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeEndElement() = writeEndElement(true)
|
|
||||||
|
|
||||||
override fun writeStartElement(name: String, ns: String, p2: String) {
|
|
||||||
xml.writeCharacters(" ".repeat(indent))
|
|
||||||
xml.writeStartElement(name, ns, p2)
|
|
||||||
indent++
|
|
||||||
content.push(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeTextNode(name: String, attrs: List<Pair<String, String>>, text: String, cdata: Boolean = true) {
|
|
||||||
xml.writeCharacters(" ".repeat(indent))
|
|
||||||
xml.writeStartElement(name)
|
|
||||||
attrs.forEach { (name, value) -> xml.writeAttribute(name, value) }
|
|
||||||
if(cdata)
|
|
||||||
xml.writeCData(text)
|
|
||||||
else
|
|
||||||
xml.writeCharacters(text)
|
|
||||||
xml.writeEndElement()
|
|
||||||
xml.writeCharacters("\n")
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,13 +24,13 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':virtualmachine')
|
|
||||||
implementation project(':codeAst')
|
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
|
implementation project(':intermediate')
|
||||||
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 +42,22 @@ sourceSets {
|
|||||||
srcDirs = ["${project.projectDir}/res"]
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDir "${project.projectDir}/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
18
codeGenIntermediate/codeGenIntermediate.iml
Normal file
18
codeGenIntermediate/codeGenIntermediate.iml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
|
<orderEntry type="module" module-name="intermediate" />
|
||||||
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,297 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.SignedDatatypes
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
|
||||||
|
|
||||||
|
internal fun translate(assignment: PtAssignment): IRCodeChunks {
|
||||||
|
if(assignment.target.children.single() is PtMachineRegister)
|
||||||
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||||
|
|
||||||
|
return if (assignment.isInplaceAssign)
|
||||||
|
translateInplaceAssign(assignment)
|
||||||
|
else
|
||||||
|
translateRegularAssign(assignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks {
|
||||||
|
val ident = assignment.target.identifier
|
||||||
|
val memory = assignment.target.memory
|
||||||
|
val array = assignment.target.array
|
||||||
|
|
||||||
|
return if(ident!=null) {
|
||||||
|
assignSelfInMemory(ident.name, assignment.value, assignment)
|
||||||
|
} else if(memory != null) {
|
||||||
|
if(memory.address is PtNumber)
|
||||||
|
assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
|
||||||
|
else
|
||||||
|
fallbackAssign(assignment)
|
||||||
|
} else if(array!=null) {
|
||||||
|
// 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)
|
||||||
|
} else {
|
||||||
|
fallbackAssign(assignment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignSelfInMemoryKnownAddress(
|
||||||
|
address: Int,
|
||||||
|
value: PtExpression,
|
||||||
|
origAssign: PtAssignment
|
||||||
|
): IRCodeChunks {
|
||||||
|
val vmDt = codeGen.irType(value.type)
|
||||||
|
when(value) {
|
||||||
|
is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
|
||||||
|
is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
|
||||||
|
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null)
|
||||||
|
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
|
||||||
|
is PtMemoryByte -> {
|
||||||
|
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
|
||||||
|
emptyList() // do nothing, mem=mem null assignment.
|
||||||
|
else {
|
||||||
|
// read and write a (i/o) memory location to itself.
|
||||||
|
val tempReg = codeGen.registers.nextFree()
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
||||||
|
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
||||||
|
listOf(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> return fallbackAssign(origAssign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignSelfInMemory(
|
||||||
|
symbol: String,
|
||||||
|
value: PtExpression,
|
||||||
|
origAssign: PtAssignment
|
||||||
|
): IRCodeChunks {
|
||||||
|
val vmDt = codeGen.irType(value.type)
|
||||||
|
return when(value) {
|
||||||
|
is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
|
||||||
|
is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
|
||||||
|
is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol)
|
||||||
|
is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
|
||||||
|
is PtMemoryByte -> {
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
val tempReg = codeGen.registers.nextFree()
|
||||||
|
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||||
|
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||||
|
listOf(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> fallbackAssign(origAssign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks {
|
||||||
|
if (codeGen.options.slowCodegenWarnings)
|
||||||
|
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||||
|
return translateRegularAssign(origAssign)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inplaceBinexpr(
|
||||||
|
operator: String,
|
||||||
|
operand: PtExpression,
|
||||||
|
vmDt: IRDataType,
|
||||||
|
signed: Boolean,
|
||||||
|
knownAddress: Int?,
|
||||||
|
symbol: String?,
|
||||||
|
origAssign: PtAssignment
|
||||||
|
): IRCodeChunks {
|
||||||
|
if(knownAddress!=null) {
|
||||||
|
when (operator) {
|
||||||
|
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"-" -> return expressionEval.operatorMinusInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"*" -> return expressionEval.operatorMultiplyInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"/" -> return expressionEval.operatorDivideInplace(knownAddress, null, vmDt, signed, operand)
|
||||||
|
"|" -> return expressionEval.operatorOrInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"&" -> return expressionEval.operatorAndInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"^" -> return expressionEval.operatorXorInplace(knownAddress, null, vmDt, operand)
|
||||||
|
"<<" -> return expressionEval.operatorShiftLeftInplace(knownAddress, null, vmDt, operand)
|
||||||
|
">>" -> return expressionEval.operatorShiftRightInplace(knownAddress, null, vmDt, signed, operand)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
symbol!!
|
||||||
|
when (operator) {
|
||||||
|
"+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand)
|
||||||
|
"-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand)
|
||||||
|
"*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand)
|
||||||
|
"/" -> return expressionEval.operatorDivideInplace(null, symbol, vmDt, signed, operand)
|
||||||
|
"|" -> return expressionEval.operatorOrInplace(null, symbol, vmDt, operand)
|
||||||
|
"&" -> return expressionEval.operatorAndInplace(null, symbol, vmDt, operand)
|
||||||
|
"^" -> return expressionEval.operatorXorInplace(null, symbol, vmDt, operand)
|
||||||
|
"<<" -> return expressionEval.operatorShiftLeftInplace(null, symbol, vmDt, operand)
|
||||||
|
">>" -> return expressionEval.operatorShiftRightInplace(null, symbol, vmDt, signed, operand)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallbackAssign(origAssign)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?): IRCodeChunks {
|
||||||
|
val code= IRCodeChunk(null, null)
|
||||||
|
when(operator) {
|
||||||
|
"+" -> { }
|
||||||
|
"-" -> {
|
||||||
|
code += if(knownAddress!=null)
|
||||||
|
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
|
||||||
|
else
|
||||||
|
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
|
||||||
|
}
|
||||||
|
"~" -> {
|
||||||
|
val regMask = codeGen.registers.nextFree()
|
||||||
|
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||||
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||||
|
code += if(knownAddress!=null)
|
||||||
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
|
||||||
|
else
|
||||||
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
|
}
|
||||||
|
return listOf(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||||
|
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
||||||
|
val ident = assignment.target.identifier
|
||||||
|
val memory = assignment.target.memory
|
||||||
|
val array = assignment.target.array
|
||||||
|
val vmDt = codeGen.irType(assignment.value.type)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
var resultRegister = -1
|
||||||
|
var resultFpRegister = -1
|
||||||
|
val zero = codeGen.isZero(assignment.value)
|
||||||
|
if(!zero) {
|
||||||
|
// calculate the assignment value
|
||||||
|
if (vmDt == IRDataType.FLOAT) {
|
||||||
|
resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||||
|
result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
|
||||||
|
} else {
|
||||||
|
resultRegister = if (assignment.value is PtMachineRegister) {
|
||||||
|
(assignment.value as PtMachineRegister).register
|
||||||
|
} else {
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
result += expressionEval.translateExpression(assignment.value, reg, -1)
|
||||||
|
reg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ident!=null) {
|
||||||
|
val instruction = if(zero) {
|
||||||
|
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name)
|
||||||
|
} else {
|
||||||
|
if (vmDt == IRDataType.FLOAT)
|
||||||
|
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name)
|
||||||
|
else
|
||||||
|
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = ident.name)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
else if(array!=null) {
|
||||||
|
val variable = array.variable.name
|
||||||
|
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.registers.nextFree()
|
||||||
|
result += expressionEval.translateExpression(array.index, idxReg, -1)
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
if(zero) {
|
||||||
|
// there's no STOREZIX instruction
|
||||||
|
resultRegister = codeGen.registers.nextFree()
|
||||||
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
||||||
|
}
|
||||||
|
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
|
||||||
|
result += code
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
val fixedIndex = constIntValue(array.index)
|
||||||
|
if(zero) {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
val offset = fixedIndex*itemsize
|
||||||
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
||||||
|
result += chunk
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.registers.nextFree()
|
||||||
|
result += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(vmDt== IRDataType.FLOAT) {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
val offset = fixedIndex*itemsize
|
||||||
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
|
||||||
|
result += chunk
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.registers.nextFree()
|
||||||
|
result += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
val offset = fixedIndex*itemsize
|
||||||
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
|
||||||
|
result += chunk
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.registers.nextFree()
|
||||||
|
result += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
else if(memory!=null) {
|
||||||
|
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.position}"}
|
||||||
|
if(zero) {
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
|
||||||
|
result += chunk
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
|
||||||
|
result += chunk
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw AssemblyError("weird assigntarget")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
|
||||||
|
return if(itemsize==1) {
|
||||||
|
expressionEval.translateExpression(array.index, indexReg, -1)
|
||||||
|
} else {
|
||||||
|
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
|
mult.children += array.index
|
||||||
|
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
|
expressionEval.translateExpression(mult, indexReg, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,404 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
|
||||||
|
|
||||||
|
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
return when(call.name) {
|
||||||
|
"any" -> funcAny(call, resultRegister)
|
||||||
|
"all" -> funcAll(call, resultRegister)
|
||||||
|
"abs" -> funcAbs(call, resultRegister)
|
||||||
|
"cmp" -> funcCmp(call)
|
||||||
|
"sgn" -> funcSgn(call, resultRegister)
|
||||||
|
"sqrt16" -> funcSqrt16(call, resultRegister)
|
||||||
|
"pop" -> funcPop(call)
|
||||||
|
"popw" -> funcPopw(call)
|
||||||
|
"push" -> funcPush(call)
|
||||||
|
"pushw" -> funcPushw(call)
|
||||||
|
"rsave",
|
||||||
|
"rsavex",
|
||||||
|
"rrestore",
|
||||||
|
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
|
||||||
|
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
|
||||||
|
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
|
||||||
|
"msb" -> funcMsb(call, resultRegister)
|
||||||
|
"lsb" -> funcLsb(call, resultRegister)
|
||||||
|
"memory" -> funcMemory(call, resultRegister)
|
||||||
|
"peek" -> funcPeek(call, resultRegister)
|
||||||
|
"peekw" -> funcPeekW(call, resultRegister)
|
||||||
|
"poke" -> funcPoke(call)
|
||||||
|
"pokew" -> funcPokeW(call)
|
||||||
|
"pokemon" -> emptyList()
|
||||||
|
"mkword" -> funcMkword(call, resultRegister)
|
||||||
|
"sort" -> funcSort(call)
|
||||||
|
"reverse" -> funcReverse(call)
|
||||||
|
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
|
||||||
|
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
|
||||||
|
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
|
||||||
|
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
|
||||||
|
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val leftRegister = codeGen.registers.nextFree()
|
||||||
|
val rightRegister = codeGen.registers.nextFree()
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], leftRegister, -1)
|
||||||
|
result += exprGen.translateExpression(call.args[1], rightRegister, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
|
val syscall =
|
||||||
|
when (array.dt) {
|
||||||
|
DataType.ARRAY_UB,
|
||||||
|
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
|
||||||
|
DataType.ARRAY_UW,
|
||||||
|
DataType.ARRAY_W -> IMSyscall.ANY_WORD
|
||||||
|
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
|
||||||
|
else -> throw IllegalArgumentException("weird type")
|
||||||
|
}
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
|
if(resultRegister!=0)
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
|
val syscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB,
|
||||||
|
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
|
||||||
|
DataType.ARRAY_UW,
|
||||||
|
DataType.ARRAY_W -> IMSyscall.ALL_WORD
|
||||||
|
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
|
||||||
|
else -> throw IllegalArgumentException("weird type")
|
||||||
|
}
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
|
if(resultRegister!=0)
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val sourceDt = call.args.single().type
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(sourceDt!=DataType.UWORD) {
|
||||||
|
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||||
|
when (sourceDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
val compareReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
||||||
|
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
|
||||||
|
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
|
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
|
||||||
|
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
val compareReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
||||||
|
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
|
||||||
|
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||||
|
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val reg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
|
val syscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
||||||
|
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
||||||
|
else -> throw IllegalArgumentException("weird type to reverse")
|
||||||
|
}
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
||||||
|
val syscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
|
||||||
|
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
|
||||||
|
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
|
||||||
|
DataType.ARRAY_W -> IMSyscall.SORT_WORD
|
||||||
|
DataType.STR -> IMSyscall.SORT_UBYTE
|
||||||
|
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
|
||||||
|
else -> throw IllegalArgumentException("weird type to sort")
|
||||||
|
}
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||||
|
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val msbReg = codeGen.registers.nextFree()
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], msbReg, -1)
|
||||||
|
result += exprGen.translateExpression(call.args[1], resultRegister, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(codeGen.isZero(call.args[1])) {
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.registers.nextFree()
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(codeGen.isZero(call.args[1])) {
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.registers.nextFree()
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
if(call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.registers.nextFree()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val name = (call.args[0] as PtString).value
|
||||||
|
val code = IRCodeChunk(null, null)
|
||||||
|
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||||
|
return listOf(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
return exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||||
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2 = resultRegister)
|
||||||
|
}
|
||||||
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
||||||
|
val vmDt = codeGen.irType(call.args[0].type)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
|
||||||
|
}
|
||||||
|
result += assignRegisterTo(call.args[0], resultRegister)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
||||||
|
val assignment = PtAssignment(target.position)
|
||||||
|
val assignTarget = PtAssignTarget(target.position)
|
||||||
|
assignTarget.children.add(target)
|
||||||
|
assignment.children.add(assignTarget)
|
||||||
|
assignment.children.add(PtMachineRegister(register, target.type, target.position))
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
result += codeGen.translateNode(assignment)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
1026
codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
Normal file
1026
codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
1269
codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt
Normal file
1269
codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,272 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||||
|
fun optimize() {
|
||||||
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
removeEmptyChunks(sub)
|
||||||
|
joinChunks(sub)
|
||||||
|
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||||
|
// we don't optimize Inline Asm chunks here.
|
||||||
|
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
||||||
|
if(chunk1 is IRCodeChunk) {
|
||||||
|
do {
|
||||||
|
val indexedInstructions = chunk1.instructions.withIndex()
|
||||||
|
.map { IndexedValue(it.index, it.value) }
|
||||||
|
val changed = removeNops(chunk1, indexedInstructions)
|
||||||
|
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|
||||||
|
|| removeUselessArithmetic(chunk1, indexedInstructions)
|
||||||
|
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||||
|
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||||
|
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||||
|
// TODO other optimizations:
|
||||||
|
// more complex optimizations such as unused registers
|
||||||
|
} while (changed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeEmptyChunks(sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
irprog.linkChunks() // re-link
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeEmptyChunks(sub: IRSubroutine) {
|
||||||
|
if(sub.chunks.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
/*
|
||||||
|
Empty Code chunk with label ->
|
||||||
|
If next chunk has no label -> move label to next chunk, remove original
|
||||||
|
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: consolidate labels into 1)
|
||||||
|
If is last chunk -> keep chunk in place because of the label.
|
||||||
|
Empty Code chunk without label ->
|
||||||
|
should not have been generated! ERROR.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
val relabelChunks = mutableListOf<Pair<Int, String>>()
|
||||||
|
val removeChunks = mutableListOf<Int>()
|
||||||
|
|
||||||
|
sub.chunks.withIndex().forEach { (index, chunk) ->
|
||||||
|
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
|
||||||
|
if(chunk.label==null) {
|
||||||
|
removeChunks += index
|
||||||
|
} else {
|
||||||
|
if (index < sub.chunks.size - 1) {
|
||||||
|
val nextchunk = sub.chunks[index + 1]
|
||||||
|
if (nextchunk.label == null) {
|
||||||
|
// can transplant label to next chunk and remove this empty one.
|
||||||
|
relabelChunks += Pair(index + 1, chunk.label!!)
|
||||||
|
removeChunks += index
|
||||||
|
} else {
|
||||||
|
if (chunk.label == nextchunk.label)
|
||||||
|
removeChunks += index
|
||||||
|
else {
|
||||||
|
// TODO: consolidate labels on same chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relabelChunks.forEach { (index, label) ->
|
||||||
|
val chunk = IRCodeChunk(label, null)
|
||||||
|
chunk.instructions += sub.chunks[index].instructions
|
||||||
|
sub.chunks[index] = chunk
|
||||||
|
}
|
||||||
|
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun joinChunks(sub: IRSubroutine) {
|
||||||
|
// Subroutine contains a list of chunks. Some can be joined into one.
|
||||||
|
|
||||||
|
if(sub.chunks.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
||||||
|
if(chunk.label!=null)
|
||||||
|
return false
|
||||||
|
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||||
|
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
|
||||||
|
val lastInstruction = previous.instructions.lastOrNull()
|
||||||
|
if(lastInstruction!=null)
|
||||||
|
return lastInstruction.opcode !in OpcodesThatJump
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val chunks = mutableListOf<IRCodeChunkBase>()
|
||||||
|
chunks += sub.chunks[0]
|
||||||
|
for(ix in 1 until sub.chunks.size) {
|
||||||
|
val lastChunk = chunks.last()
|
||||||
|
if(mayJoin(lastChunk, sub.chunks[ix])) {
|
||||||
|
lastChunk.instructions += sub.chunks[ix].instructions
|
||||||
|
lastChunk.next = sub.chunks[ix].next
|
||||||
|
}
|
||||||
|
else
|
||||||
|
chunks += sub.chunks[ix]
|
||||||
|
}
|
||||||
|
sub.chunks.clear()
|
||||||
|
sub.chunks += chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): 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 < chunk.instructions.size-1) {
|
||||||
|
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
||||||
|
if(insAfter!=null && insAfter.opcode == Opcode.POP) {
|
||||||
|
if(ins.reg1==insAfter.reg1) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
} else {
|
||||||
|
chunk.instructions[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
|
||||||
|
chunk.instructions.removeAt(idx+1)
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): 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 < chunk.instructions.size-1) {
|
||||||
|
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
||||||
|
if(insAfter?.opcode == ins.opcode) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeWeirdBranches(chunk: IRCodeChunk, nextChunk: IRCodeChunkBase?, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
val labelSymbol = ins.labelSymbol
|
||||||
|
|
||||||
|
// remove jump/branch to label immediately below (= next chunk if it has that label)
|
||||||
|
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
|
||||||
|
if(idx==chunk.instructions.size-1 && ins.branchTarget===nextChunk) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove useless RETURN
|
||||||
|
if(ins.opcode == Opcode.RETURN && idx>0) {
|
||||||
|
val previous = chunk.instructions[idx-1] as? IRInstruction
|
||||||
|
if(previous?.opcode in OpcodesThatJump) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||||
|
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
when (ins.opcode) {
|
||||||
|
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||||
|
if (ins.value == 1) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.ADD, Opcode.SUB -> {
|
||||||
|
if (ins.value == 1) {
|
||||||
|
chunk.instructions[idx] = IRInstruction(
|
||||||
|
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
||||||
|
ins.type,
|
||||||
|
ins.reg1
|
||||||
|
)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 0) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.AND -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 255 && ins.type == IRDataType.BYTE) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
} else if (ins.value == 65535 && ins.type == IRDataType.WORD) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.OR -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
} else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) {
|
||||||
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode.XOR -> {
|
||||||
|
if (ins.value == 0) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||||
|
var changed = false
|
||||||
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
|
if (ins.opcode == Opcode.NOP) {
|
||||||
|
changed = true
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): 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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
|
||||||
|
fun optimize(): Int {
|
||||||
|
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||||
|
|
||||||
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
sub.chunks.forEach { chunk ->
|
||||||
|
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
||||||
|
|
||||||
|
// remove empty subs
|
||||||
|
irprog.blocks.forEach { block ->
|
||||||
|
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||||
|
if(sub.isEmpty()) {
|
||||||
|
if(!sub.position.file.startsWith(libraryFilePrefix))
|
||||||
|
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||||
|
block.children.remove(sub)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove empty blocks
|
||||||
|
irprog.blocks.reversed().forEach { block ->
|
||||||
|
if(block.isEmpty()) {
|
||||||
|
irprog.blocks.remove(block)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
||||||
|
val entrypointSub = irprog.blocks.single { it.name=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||||
|
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||||
|
|
||||||
|
fun grow() {
|
||||||
|
val new = mutableSetOf<IRCodeChunkBase>()
|
||||||
|
reachable.forEach {
|
||||||
|
it.next?.let { next -> new += next }
|
||||||
|
it.instructions.forEach { instr ->
|
||||||
|
if (instr.branchTarget == null)
|
||||||
|
instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
|
||||||
|
else
|
||||||
|
new += instr.branchTarget!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reachable += new
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousCount = reachable.size
|
||||||
|
while(true) {
|
||||||
|
grow()
|
||||||
|
if(reachable.size<=previousCount)
|
||||||
|
break
|
||||||
|
previousCount = reachable.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeUnlinkedChunks(reachable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
|
||||||
|
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
sub.chunks.forEach { chunk ->
|
||||||
|
chunk.next?.let { next -> linkedChunks += next }
|
||||||
|
chunk.instructions.forEach {
|
||||||
|
if(it.branchTarget==null) {
|
||||||
|
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
|
||||||
|
} else {
|
||||||
|
linkedChunks += it.branchTarget!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chunk.label == "main.start")
|
||||||
|
linkedChunks += chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeUnlinkedChunks(linkedChunks)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnlinkedChunks(
|
||||||
|
linkedChunks: MutableSet<IRCodeChunkBase>
|
||||||
|
): Int {
|
||||||
|
var numRemoved = 0
|
||||||
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
||||||
|
if (chunk !in linkedChunks) {
|
||||||
|
if (chunk === sub.chunks[0]) {
|
||||||
|
when(chunk) {
|
||||||
|
is IRCodeChunk -> {
|
||||||
|
if (chunk.isNotEmpty()) {
|
||||||
|
// don't remove the first chunk of the sub itself because it has to have the name of the sub as label
|
||||||
|
chunk.instructions.clear()
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IRInlineAsmChunk, is IRInlineBinaryChunk -> {
|
||||||
|
sub.chunks[index] = IRCodeChunk(chunk.label, chunk.next)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sub.chunks.removeAt(index)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numRemoved
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.intermediate.SyscallRegisterBase
|
||||||
|
|
||||||
|
internal class RegisterPool {
|
||||||
|
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
||||||
|
private var firstFree: Int=3
|
||||||
|
private var firstFreeFloat: Int=3
|
||||||
|
|
||||||
|
fun peekNext() = firstFree
|
||||||
|
fun peekNextFloat() = firstFreeFloat
|
||||||
|
|
||||||
|
fun nextFree(): Int {
|
||||||
|
val result = firstFree
|
||||||
|
firstFree++
|
||||||
|
if(firstFree >= SyscallRegisterBase)
|
||||||
|
throw AssemblyError("out of virtual registers (int)")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextFreeFloat(): Int {
|
||||||
|
val result = firstFreeFloat
|
||||||
|
firstFreeFloat++
|
||||||
|
if(firstFreeFloat >= SyscallRegisterBase)
|
||||||
|
throw AssemblyError("out of virtual registers (fp)")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
35
codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt
Normal file
35
codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package prog8.codegen.vm
|
||||||
|
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.IAssemblyGenerator
|
||||||
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
|
import prog8.intermediate.IRFileWriter
|
||||||
|
import prog8.intermediate.IRProgram
|
||||||
|
|
||||||
|
class VmCodeGen(private val program: PtProgram,
|
||||||
|
private val symbolTable: SymbolTable,
|
||||||
|
private val options: CompilationOptions,
|
||||||
|
private val errors: IErrorReporter
|
||||||
|
): IAssemblyGenerator {
|
||||||
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
|
|
||||||
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
|
val irProgram = irCodeGen.generate()
|
||||||
|
|
||||||
|
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
|
||||||
|
|
||||||
|
override fun assemble(options: CompilationOptions): Boolean {
|
||||||
|
// the VM reads the IR file from disk.
|
||||||
|
IRFileWriter(irProgram, null).write()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
20
codeGenIntermediate/test/Dummies.kt
Normal file
20
codeGenIntermediate/test/Dummies.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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 ""
|
||||||
|
}
|
||||||
|
}
|
189
codeGenIntermediate/test/TestIRPeepholeOpt.kt
Normal file
189
codeGenIntermediate/test/TestIRPeepholeOpt.kt
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
import prog8.codegen.intermediate.IRPeepholeOptimizer
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
class TestIRPeepholeOpt: FunSpec({
|
||||||
|
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||||
|
require(chunks.first().label=="main.start")
|
||||||
|
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||||
|
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||||
|
chunks.forEach { sub += it }
|
||||||
|
block += sub
|
||||||
|
val target = VMTarget()
|
||||||
|
val options = CompilationOptions(
|
||||||
|
OutputType.RAW,
|
||||||
|
CbmPrgLauncherType.NONE,
|
||||||
|
ZeropageType.DONTUSE,
|
||||||
|
emptyList(),
|
||||||
|
floats = false,
|
||||||
|
noSysInit = true,
|
||||||
|
compTarget = target,
|
||||||
|
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||||
|
)
|
||||||
|
val prog = IRProgram("test", IRSymbolTable(null), options, target)
|
||||||
|
prog.addBlock(block)
|
||||||
|
prog.linkChunks()
|
||||||
|
prog.validate()
|
||||||
|
return prog
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeIRProgram(instructions: List<IRInstruction>): IRProgram {
|
||||||
|
val chunk = IRCodeChunk("main.start", null)
|
||||||
|
instructions.forEach { chunk += it }
|
||||||
|
return makeIRProgram(listOf(chunk))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
|
||||||
|
|
||||||
|
test("remove nops") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
|
||||||
|
IRInstruction(Opcode.NOP),
|
||||||
|
IRInstruction(Opcode.NOP)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 3
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove jmp to label below") {
|
||||||
|
val c1 = IRCodeChunk("main.start", null)
|
||||||
|
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label
|
||||||
|
val c2 = IRCodeChunk("label", null)
|
||||||
|
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label
|
||||||
|
c2 += IRInstruction(Opcode.NOP) // removed
|
||||||
|
val c3 = IRCodeChunk("label2", null)
|
||||||
|
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
|
||||||
|
c3 += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1)
|
||||||
|
val c4 = IRCodeChunk("label3", null)
|
||||||
|
val irProg = makeIRProgram(listOf(c1, c2, c3, c4))
|
||||||
|
|
||||||
|
irProg.chunks().size shouldBe 4
|
||||||
|
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
irProg.chunks().size shouldBe 4
|
||||||
|
irProg.chunks()[0].label shouldBe "main.start"
|
||||||
|
irProg.chunks()[1].label shouldBe "label"
|
||||||
|
irProg.chunks()[2].label shouldBe "label2"
|
||||||
|
irProg.chunks()[3].label shouldBe "label3"
|
||||||
|
irProg.chunks()[0].isEmpty() shouldBe true
|
||||||
|
irProg.chunks()[1].isEmpty() shouldBe true
|
||||||
|
irProg.chunks()[2].isEmpty() shouldBe false
|
||||||
|
irProg.chunks()[3].isEmpty() shouldBe true
|
||||||
|
val instr = irProg.chunks().flatMap { it.instructions }
|
||||||
|
instr.size shouldBe 2
|
||||||
|
instr[0].opcode shouldBe Opcode.JUMP
|
||||||
|
instr[1].opcode shouldBe Opcode.INC
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove double sec/clc") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.SEC),
|
||||||
|
IRInstruction(Opcode.SEC),
|
||||||
|
IRInstruction(Opcode.SEC),
|
||||||
|
IRInstruction(Opcode.CLC),
|
||||||
|
IRInstruction(Opcode.CLC),
|
||||||
|
IRInstruction(Opcode.CLC)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 6
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
val instr = irProg.chunks().single().instructions
|
||||||
|
instr.size shouldBe 1
|
||||||
|
instr[0].opcode shouldBe Opcode.CLC
|
||||||
|
}
|
||||||
|
|
||||||
|
test("push followed by pop") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=42),
|
||||||
|
IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=42),
|
||||||
|
IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=99),
|
||||||
|
IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
val instr = irProg.chunks().single().instructions
|
||||||
|
instr.size shouldBe 1
|
||||||
|
instr[0].opcode shouldBe Opcode.LOADR
|
||||||
|
instr[0].reg1 shouldBe 222
|
||||||
|
instr[0].reg2 shouldBe 99
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove useless div/mul, add/sub") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2),
|
||||||
|
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2),
|
||||||
|
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2),
|
||||||
|
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2),
|
||||||
|
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
|
||||||
|
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 10
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("replace add/sub 1 by inc/dec") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 2
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
val instr = irProg.chunks().single().instructions
|
||||||
|
instr.size shouldBe 2
|
||||||
|
instr[0].opcode shouldBe Opcode.INC
|
||||||
|
instr[1].opcode shouldBe Opcode.DEC
|
||||||
|
}
|
||||||
|
|
||||||
|
test("remove useless and/or/xor") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255),
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535),
|
||||||
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0),
|
||||||
|
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0),
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200),
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000),
|
||||||
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
|
||||||
|
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 8
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("replace and/or/xor by constant number") {
|
||||||
|
val irProg = makeIRProgram(listOf(
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0),
|
||||||
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0),
|
||||||
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
|
||||||
|
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
|
||||||
|
))
|
||||||
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
|
opt.optimize()
|
||||||
|
val instr = irProg.chunks().single().instructions
|
||||||
|
instr.size shouldBe 4
|
||||||
|
instr[0].opcode shouldBe Opcode.LOAD
|
||||||
|
instr[1].opcode shouldBe Opcode.LOAD
|
||||||
|
instr[2].opcode shouldBe Opcode.LOAD
|
||||||
|
instr[3].opcode shouldBe Opcode.LOAD
|
||||||
|
instr[0].value shouldBe 0
|
||||||
|
instr[1].value shouldBe 0
|
||||||
|
instr[2].value shouldBe 255
|
||||||
|
instr[3].value shouldBe 65535
|
||||||
|
}
|
||||||
|
})
|
@ -1,16 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="JAVA_MODULE" version="4">
|
<module version="4">
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
</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="michael.bull.kotlin.result.jvm" level="project" />
|
|
||||||
<orderEntry type="module" module-name="codeAst" />
|
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
|
||||||
<orderEntry type="module" module-name="virtualmachine" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,84 +0,0 @@
|
|||||||
package prog8.codegen.virtual
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.vm.Instruction
|
|
||||||
import prog8.vm.Opcode
|
|
||||||
import prog8.vm.VmDataType
|
|
||||||
import java.io.BufferedWriter
|
|
||||||
import kotlin.io.path.bufferedWriter
|
|
||||||
import kotlin.io.path.div
|
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyProgram(override val name: String,
|
|
||||||
private val allocations: VariableAllocator
|
|
||||||
) : IAssemblyProgram {
|
|
||||||
|
|
||||||
private val globalInits = mutableListOf<VmCodeLine>()
|
|
||||||
private val blocks = mutableListOf<VmCodeChunk>()
|
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
|
||||||
val outfile = options.outputDir / ("$name.p8virt")
|
|
||||||
println("write code to $outfile")
|
|
||||||
outfile.bufferedWriter().use { out ->
|
|
||||||
allocations.asVmMemory().forEach { (name, alloc) ->
|
|
||||||
out.write("; ${name.joinToString(".")}\n")
|
|
||||||
out.write(alloc + "\n")
|
|
||||||
}
|
|
||||||
out.write("------PROGRAM------\n")
|
|
||||||
|
|
||||||
out.write("; global var inits\n")
|
|
||||||
globalInits.forEach { out.writeLine(it) }
|
|
||||||
|
|
||||||
out.write("; actual program code\n")
|
|
||||||
blocks.asSequence().flatMap { it.lines }.forEach { line->out.writeLine(line) }
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BufferedWriter.writeLine(line: VmCodeLine) {
|
|
||||||
when(line) {
|
|
||||||
is VmCodeComment -> write("; ${line.comment}\n")
|
|
||||||
is VmCodeInstruction -> {
|
|
||||||
write(line.ins.toString() + "\n")
|
|
||||||
}
|
|
||||||
is VmCodeLabel -> write("_" + line.name.joinToString(".") + ":\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
|
|
||||||
fun addBlock(block: VmCodeChunk) = blocks.add(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class VmCodeLine
|
|
||||||
|
|
||||||
internal class VmCodeInstruction(
|
|
||||||
opcode: Opcode,
|
|
||||||
type: VmDataType?=null,
|
|
||||||
reg1: Int?=null, // 0-$ffff
|
|
||||||
reg2: Int?=null, // 0-$ffff
|
|
||||||
reg3: Int?=null, // 0-$ffff
|
|
||||||
value: Int?=null, // 0-$ffff
|
|
||||||
symbol: List<String>?=null // alternative to value
|
|
||||||
): VmCodeLine() {
|
|
||||||
val ins = Instruction(opcode, type, reg1, reg2, reg3, value, symbol)
|
|
||||||
}
|
|
||||||
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
|
|
||||||
internal class VmCodeComment(val comment: String): VmCodeLine()
|
|
||||||
|
|
||||||
internal class VmCodeChunk(initial: VmCodeLine? = null) {
|
|
||||||
val lines = mutableListOf<VmCodeLine>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(initial!=null)
|
|
||||||
lines.add(initial)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun plusAssign(line: VmCodeLine) {
|
|
||||||
lines.add(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun plusAssign(chunk: VmCodeChunk) {
|
|
||||||
lines.addAll(chunk.lines)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
package prog8.codegen.virtual
|
|
||||||
|
|
||||||
import prog8.code.ast.PtBuiltinFunctionCall
|
|
||||||
import prog8.code.ast.PtNumber
|
|
||||||
import prog8.code.ast.PtString
|
|
||||||
import prog8.vm.Opcode
|
|
||||||
import prog8.vm.Syscall
|
|
||||||
import prog8.vm.VmDataType
|
|
||||||
|
|
||||||
internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) {
|
|
||||||
|
|
||||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
when(call.name) {
|
|
||||||
"syscall" -> {
|
|
||||||
val vExpr = call.args.single() as PtNumber
|
|
||||||
code += VmCodeInstruction(Opcode.SYSCALL, value=vExpr.number.toInt())
|
|
||||||
}
|
|
||||||
"syscall1" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
|
|
||||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
|
||||||
code += exprGen.translateExpression(call.args[1], 0)
|
|
||||||
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
|
|
||||||
}
|
|
||||||
"syscall2" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
|
|
||||||
while(codeGen.vmRegisters.peekNext()<2) {
|
|
||||||
codeGen.vmRegisters.nextFree()
|
|
||||||
}
|
|
||||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
|
||||||
code += exprGen.translateExpression(call.args[1], 0)
|
|
||||||
code += exprGen.translateExpression(call.args[2], 1)
|
|
||||||
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
|
|
||||||
}
|
|
||||||
"syscall3" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
|
|
||||||
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)
|
|
||||||
while(codeGen.vmRegisters.peekNext()<3) {
|
|
||||||
codeGen.vmRegisters.nextFree()
|
|
||||||
}
|
|
||||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
|
||||||
code += exprGen.translateExpression(call.args[1], 0)
|
|
||||||
code += exprGen.translateExpression(call.args[2], 1)
|
|
||||||
code += exprGen.translateExpression(call.args[3], 2)
|
|
||||||
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
|
|
||||||
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
|
|
||||||
}
|
|
||||||
"msb" -> {
|
|
||||||
code += exprGen.translateExpression(call.args.single(), resultRegister)
|
|
||||||
code += VmCodeInstruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister)
|
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
|
||||||
}
|
|
||||||
"lsb" -> {
|
|
||||||
code += exprGen.translateExpression(call.args.single(), resultRegister)
|
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
|
||||||
}
|
|
||||||
"memory" -> {
|
|
||||||
val name = (call.args[0] as PtString).value
|
|
||||||
val size = (call.args[1] as PtNumber).number.toUInt()
|
|
||||||
val align = (call.args[2] as PtNumber).number.toUInt()
|
|
||||||
val existing = codeGen.allocations.getMemorySlab(name)
|
|
||||||
val address = if(existing==null)
|
|
||||||
codeGen.allocations.allocateMemorySlab(name, size, align)
|
|
||||||
else if(existing.second!=size || existing.third!=align) {
|
|
||||||
codeGen.errors.err("memory slab '$name' already exists with a different size or alignment", call.position)
|
|
||||||
return VmCodeChunk()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
existing.first
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt())
|
|
||||||
}
|
|
||||||
"rnd" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal)
|
|
||||||
if(resultRegister!=0)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
|
|
||||||
}
|
|
||||||
"peek" -> {
|
|
||||||
val addressReg = codeGen.vmRegisters.nextFree()
|
|
||||||
code += exprGen.translateExpression(call.args.single(), addressReg)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg)
|
|
||||||
}
|
|
||||||
"peekw" -> {
|
|
||||||
val addressReg = codeGen.vmRegisters.nextFree()
|
|
||||||
code += exprGen.translateExpression(call.args.single(), addressReg)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg)
|
|
||||||
}
|
|
||||||
"mkword" -> {
|
|
||||||
val msbReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val lsbReg = codeGen.vmRegisters.nextFree()
|
|
||||||
code += exprGen.translateExpression(call.args[0], msbReg)
|
|
||||||
code += exprGen.translateExpression(call.args[1], lsbReg)
|
|
||||||
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
TODO("builtinfunc ${call.name}")
|
|
||||||
// code += VmCodeInstruction(Opcode.NOP))
|
|
||||||
// for (arg in call.args) {
|
|
||||||
// code += translateExpression(arg, resultRegister)
|
|
||||||
// code += when(arg.type) {
|
|
||||||
// in ByteDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister))
|
|
||||||
// in WordDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister))
|
|
||||||
// else -> throw AssemblyError("weird arg dt")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// code += VmCodeInstruction(Opcode.CALL), labelArg = listOf("_prog8_builtin", call.name))
|
|
||||||
// for (arg in call.args) {
|
|
||||||
// code += when(arg.type) {
|
|
||||||
// in ByteDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister))
|
|
||||||
// in WordDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister))
|
|
||||||
// else -> throw AssemblyError("weird arg dt")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// code += VmCodeInstruction(Opcode.NOP))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,593 +0,0 @@
|
|||||||
package prog8.codegen.virtual
|
|
||||||
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.ast.*
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.vm.Opcode
|
|
||||||
import prog8.vm.VmDataType
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
|
|
||||||
internal class VmRegisterPool {
|
|
||||||
private var firstFree: Int=3 // registers 0,1,2 are reserved
|
|
||||||
|
|
||||||
fun peekNext() = firstFree
|
|
||||||
|
|
||||||
fun nextFree(): Int {
|
|
||||||
val result = firstFree
|
|
||||||
firstFree++
|
|
||||||
if(firstFree>65535)
|
|
||||||
throw AssemblyError("out of virtual registers")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CodeGen(internal val program: PtProgram,
|
|
||||||
internal val symbolTable: SymbolTable,
|
|
||||||
internal val options: CompilationOptions,
|
|
||||||
internal val errors: IErrorReporter
|
|
||||||
): IAssemblyGenerator {
|
|
||||||
|
|
||||||
internal val allocations = VariableAllocator(symbolTable, program, errors)
|
|
||||||
private val expressionEval = ExpressionGen(this)
|
|
||||||
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
|
||||||
internal val vmRegisters = VmRegisterPool()
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(options.dontReinitGlobals)
|
|
||||||
TODO("support no globals re-init in vm")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
|
||||||
val vmprog = AssemblyProgram(program.name, allocations)
|
|
||||||
|
|
||||||
// collect global variables initializers
|
|
||||||
program.allBlocks().forEach {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign) }
|
|
||||||
vmprog.addGlobalInits(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (block in program.allBlocks()) {
|
|
||||||
vmprog.addBlock(translate(block))
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
|
|
||||||
|
|
||||||
return vmprog
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun translateNode(node: PtNode): VmCodeChunk {
|
|
||||||
val code = when(node) {
|
|
||||||
is PtBlock -> translate(node)
|
|
||||||
is PtSub -> translate(node)
|
|
||||||
is PtScopeVarsDecls -> VmCodeChunk() // vars 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 PtConstant -> VmCodeChunk() // constants have all been folded into the code
|
|
||||||
is PtAssignment -> translate(node)
|
|
||||||
is PtNodeGroup -> translateGroup(node.children)
|
|
||||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
|
||||||
is PtFunctionCall -> expressionEval.translate(node, 0)
|
|
||||||
is PtNop -> VmCodeChunk()
|
|
||||||
is PtReturn -> translate(node)
|
|
||||||
is PtJump -> translate(node)
|
|
||||||
is PtWhen -> translate(node)
|
|
||||||
is PtPipe -> expressionEval.translate(node, 0)
|
|
||||||
is PtForLoop -> translate(node)
|
|
||||||
is PtIfElse -> translate(node)
|
|
||||||
is PtPostIncrDecr -> translate(node)
|
|
||||||
is PtRepeatLoop -> translate(node)
|
|
||||||
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
|
|
||||||
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Opcode.BREAKPOINT))
|
|
||||||
is PtAddressOf,
|
|
||||||
is PtContainmentCheck,
|
|
||||||
is PtMemoryByte,
|
|
||||||
is PtProgram,
|
|
||||||
is PtArrayIndexer,
|
|
||||||
is PtBinaryExpression,
|
|
||||||
is PtIdentifier,
|
|
||||||
is PtWhenChoice,
|
|
||||||
is PtPrefix,
|
|
||||||
is PtRange,
|
|
||||||
is PtAssignTarget,
|
|
||||||
is PtTypeCast,
|
|
||||||
is PtSubroutineParameter,
|
|
||||||
is PtNumber,
|
|
||||||
is PtArray,
|
|
||||||
is PtString -> throw AssemblyError("strings should not occur as separate statement node ${node.position}")
|
|
||||||
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target ${node.position}")
|
|
||||||
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target ${node.position}")
|
|
||||||
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target ${node.position}")
|
|
||||||
is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}")
|
|
||||||
else -> TODO("missing codegen for $node")
|
|
||||||
}
|
|
||||||
if(code.lines.isNotEmpty() && node.position.line!=0)
|
|
||||||
code.lines.add(0, VmCodeComment(node.position.toString()))
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(whenStmt: PtWhen): VmCodeChunk {
|
|
||||||
if(whenStmt.choices.children.isEmpty())
|
|
||||||
return VmCodeChunk()
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val valueReg = vmRegisters.nextFree()
|
|
||||||
val choiceReg = vmRegisters.nextFree()
|
|
||||||
val valueDt = vmType(whenStmt.value.type)
|
|
||||||
code += expressionEval.translateExpression(whenStmt.value, valueReg)
|
|
||||||
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
|
|
||||||
val endLabel = createLabelName()
|
|
||||||
for (choice in choices) {
|
|
||||||
if(choice.isElse) {
|
|
||||||
code += translateNode(choice.statements)
|
|
||||||
} else {
|
|
||||||
val skipLabel = createLabelName()
|
|
||||||
val values = choice.values.children.map {it as PtNumber}
|
|
||||||
if(values.size==1) {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, symbol = skipLabel)
|
|
||||||
code += translateNode(choice.statements)
|
|
||||||
if(choice.statements.children.last() !is PtReturn)
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel)
|
|
||||||
} else {
|
|
||||||
val matchLabel = createLabelName()
|
|
||||||
for (value in values) {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, symbol = matchLabel)
|
|
||||||
}
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = skipLabel)
|
|
||||||
code += VmCodeLabel(matchLabel)
|
|
||||||
code += translateNode(choice.statements)
|
|
||||||
if(choice.statements.children.last() !is PtReturn)
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel)
|
|
||||||
}
|
|
||||||
code += VmCodeLabel(skipLabel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
code += VmCodeLabel(endLabel)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(forLoop: PtForLoop): VmCodeChunk {
|
|
||||||
val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable
|
|
||||||
val iterable = forLoop.iterable
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
when(iterable) {
|
|
||||||
is PtRange -> {
|
|
||||||
if(iterable.from is PtNumber && iterable.to is PtNumber)
|
|
||||||
code += translateForInConstantRange(forLoop, loopvar)
|
|
||||||
else
|
|
||||||
code += translateForInNonConstantRange(forLoop, loopvar)
|
|
||||||
}
|
|
||||||
is PtIdentifier -> {
|
|
||||||
val arrayAddress = allocations.get(iterable.targetName)
|
|
||||||
val iterableVar = symbolTable.lookup(iterable.targetName) as StStaticVariable
|
|
||||||
val loopvarAddress = allocations.get(loopvar.scopedName)
|
|
||||||
val indexReg = vmRegisters.nextFree()
|
|
||||||
val loopLabel = createLabelName()
|
|
||||||
val endLabel = createLabelName()
|
|
||||||
if(iterableVar.dt==DataType.STR) {
|
|
||||||
// iterate over a zero-terminated string
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
|
||||||
code += VmCodeLabel(loopLabel)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)
|
|
||||||
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)
|
|
||||||
code += translateNode(forLoop.statements)
|
|
||||||
code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
|
|
||||||
code += VmCodeLabel(endLabel)
|
|
||||||
} else {
|
|
||||||
// iterate over array
|
|
||||||
val elementDt = ArrayToElementTypes.getValue(iterable.type)
|
|
||||||
val elementSize = program.memsizer.memorySize(elementDt)
|
|
||||||
val lengthBytes = iterableVar.length!! * elementSize
|
|
||||||
val lengthReg = vmRegisters.nextFree()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
|
|
||||||
code += VmCodeLabel(loopLabel)
|
|
||||||
code += VmCodeInstruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
|
|
||||||
code += translateNode(forLoop.statements)
|
|
||||||
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
|
|
||||||
code += VmCodeLabel(endLabel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird for iterable")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
|
|
||||||
val iterable = forLoop.iterable as PtRange
|
|
||||||
val step = iterable.step.number.toInt()
|
|
||||||
if (step==0)
|
|
||||||
throw AssemblyError("step 0")
|
|
||||||
val indexReg = vmRegisters.nextFree()
|
|
||||||
val endvalueReg = vmRegisters.nextFree()
|
|
||||||
val loopvarAddress = allocations.get(loopvar.scopedName)
|
|
||||||
val loopvarDt = vmType(loopvar.dt)
|
|
||||||
val loopLabel = createLabelName()
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
|
|
||||||
code += expressionEval.translateExpression(iterable.to, endvalueReg)
|
|
||||||
code += expressionEval.translateExpression(iterable.from, indexReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
|
||||||
code += VmCodeLabel(loopLabel)
|
|
||||||
code += translateNode(forLoop.statements)
|
|
||||||
if(step<3) {
|
|
||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
|
||||||
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
|
|
||||||
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
|
|
||||||
val iterable = forLoop.iterable as PtRange
|
|
||||||
val step = iterable.step.number.toInt()
|
|
||||||
val range = IntProgression.fromClosedRange(
|
|
||||||
(iterable.from as PtNumber).number.toInt(),
|
|
||||||
(iterable.to as PtNumber).number.toInt() + step,
|
|
||||||
step)
|
|
||||||
if (range.isEmpty() || range.step==0)
|
|
||||||
throw AssemblyError("empty range or step 0")
|
|
||||||
|
|
||||||
val loopLabel = createLabelName()
|
|
||||||
val loopvarAddress = allocations.get(loopvar.scopedName)
|
|
||||||
val indexReg = vmRegisters.nextFree()
|
|
||||||
val endvalueReg = vmRegisters.nextFree()
|
|
||||||
val loopvarDt = vmType(loopvar.dt)
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=endvalueReg, value=range.last)
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=range.first)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
|
|
||||||
code += VmCodeLabel(loopLabel)
|
|
||||||
code += translateNode(forLoop.statements)
|
|
||||||
if(range.step<3) {
|
|
||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), range.step)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
} else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
code += addConstReg(loopvarDt, indexReg, range.step)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
|
||||||
}
|
|
||||||
// TODO more optimal loop instruction for loops ending on 0 (BNZ?)
|
|
||||||
code += VmCodeInstruction(Opcode.BNE, loopvarDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addConstReg(dt: VmDataType, reg: Int, value: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
when(value) {
|
|
||||||
0 -> { /* do nothing */ }
|
|
||||||
1 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
|
|
||||||
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
|
|
||||||
}
|
|
||||||
-1 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
|
||||||
}
|
|
||||||
-2 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
|
||||||
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val valueReg = vmRegisters.nextFree()
|
|
||||||
if(value>0) {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
|
|
||||||
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = reg, reg3 = valueReg)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
|
|
||||||
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = reg, reg3 = valueReg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addConstMem(dt: VmDataType, address: UInt, value: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
when(value) {
|
|
||||||
0 -> { /* do nothing */ }
|
|
||||||
1 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
|
|
||||||
}
|
|
||||||
-1 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
|
|
||||||
}
|
|
||||||
-2 -> {
|
|
||||||
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val valueReg = vmRegisters.nextFree()
|
|
||||||
val operandReg = vmRegisters.nextFree()
|
|
||||||
if(value>0) {
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
|
|
||||||
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
|
|
||||||
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
|
|
||||||
|
|
||||||
private fun multiplyByConst(dt: VmDataType, reg: Int, factor: UInt): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val pow2 = powersOfTwo.indexOf(factor.toInt())
|
|
||||||
if(pow2>=1) {
|
|
||||||
// just shift bits
|
|
||||||
code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2)
|
|
||||||
} else {
|
|
||||||
when(factor) {
|
|
||||||
0u -> {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
|
||||||
}
|
|
||||||
1u -> { /* do nothing */ }
|
|
||||||
else -> {
|
|
||||||
val factorReg = vmRegisters.nextFree()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())
|
|
||||||
code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(ifElse: PtIfElse): VmCodeChunk {
|
|
||||||
var branch = Opcode.BZ
|
|
||||||
var condition = ifElse.condition
|
|
||||||
|
|
||||||
val cond = ifElse.condition as? PtBinaryExpression
|
|
||||||
if((cond?.right as? PtNumber)?.number==0.0) {
|
|
||||||
if(cond.operator == "==") {
|
|
||||||
// if X==0 ... so we branch on Not-zero instead.
|
|
||||||
branch = Opcode.BNZ
|
|
||||||
condition = cond.left
|
|
||||||
}
|
|
||||||
else if(cond.operator == "!=") {
|
|
||||||
// if X!=0 ... so we keep branching on Zero.
|
|
||||||
condition = cond.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val conditionReg = vmRegisters.nextFree()
|
|
||||||
val vmDt = vmType(condition.type)
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += expressionEval.translateExpression(condition, conditionReg)
|
|
||||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
|
||||||
// if and else parts
|
|
||||||
val elseLabel = createLabelName()
|
|
||||||
val afterIfLabel = createLabelName()
|
|
||||||
code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel)
|
|
||||||
code += translateNode(ifElse.ifScope)
|
|
||||||
code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel)
|
|
||||||
code += VmCodeLabel(elseLabel)
|
|
||||||
code += translateNode(ifElse.elseScope)
|
|
||||||
code += VmCodeLabel(afterIfLabel)
|
|
||||||
} else {
|
|
||||||
// only if part
|
|
||||||
val afterIfLabel = createLabelName()
|
|
||||||
code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel)
|
|
||||||
code += translateNode(ifElse.ifScope)
|
|
||||||
code += VmCodeLabel(afterIfLabel)
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(postIncrDecr: PtPostIncrDecr): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val operation = when(postIncrDecr.operator) {
|
|
||||||
"++" -> Opcode.INC
|
|
||||||
"--" -> Opcode.DEC
|
|
||||||
else -> throw AssemblyError("weird operator")
|
|
||||||
}
|
|
||||||
val ident = postIncrDecr.target.identifier
|
|
||||||
val memory = postIncrDecr.target.memory
|
|
||||||
val array = postIncrDecr.target.array
|
|
||||||
val vmDt = vmType(postIncrDecr.target.type)
|
|
||||||
val resultReg = vmRegisters.nextFree()
|
|
||||||
if(ident!=null) {
|
|
||||||
val address = allocations.get(ident.targetName)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)
|
|
||||||
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)
|
|
||||||
} else if(memory!=null) {
|
|
||||||
val addressReg = vmRegisters.nextFree()
|
|
||||||
code += expressionEval.translateExpression(memory.address, addressReg)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)
|
|
||||||
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)
|
|
||||||
} else if (array!=null) {
|
|
||||||
val variable = array.variable.targetName
|
|
||||||
var variableAddr = allocations.get(variable)
|
|
||||||
val itemsize = program.memsizer.memorySize(array.type)
|
|
||||||
val fixedIndex = (array.index as? PtNumber)?.number?.toInt()
|
|
||||||
val memOp = when(postIncrDecr.operator) {
|
|
||||||
"++" -> Opcode.INCM
|
|
||||||
"--" -> Opcode.DECM
|
|
||||||
else -> throw AssemblyError("weird operator")
|
|
||||||
}
|
|
||||||
if(fixedIndex!=null) {
|
|
||||||
variableAddr += fixedIndex*itemsize
|
|
||||||
code += VmCodeInstruction(memOp, vmDt, value=variableAddr)
|
|
||||||
} else {
|
|
||||||
val indexReg = vmRegisters.nextFree()
|
|
||||||
code += expressionEval.translateExpression(array.index, indexReg)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultReg, reg2=indexReg, value=variableAddr)
|
|
||||||
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1=resultReg, reg2=indexReg, value=variableAddr)
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw AssemblyError("weird assigntarget")
|
|
||||||
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(repeat: PtRepeatLoop): VmCodeChunk {
|
|
||||||
if((repeat.count as? PtNumber)?.number==0.0)
|
|
||||||
return VmCodeChunk()
|
|
||||||
if((repeat.count as? PtNumber)?.number==1.0)
|
|
||||||
return translateGroup(repeat.children)
|
|
||||||
if((repeat.count as? PtNumber)?.number==256.0) {
|
|
||||||
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
|
|
||||||
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val counterReg = vmRegisters.nextFree()
|
|
||||||
val vmDt = vmType(repeat.count.type)
|
|
||||||
code += expressionEval.translateExpression(repeat.count, counterReg)
|
|
||||||
val repeatLabel = createLabelName()
|
|
||||||
code += VmCodeLabel(repeatLabel)
|
|
||||||
code += translateNode(repeat.statements)
|
|
||||||
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
|
|
||||||
code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(jump: PtJump): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
if(jump.address!=null)
|
|
||||||
throw AssemblyError("cannot jump to memory location in the vm target")
|
|
||||||
code += if(jump.generatedLabel!=null)
|
|
||||||
VmCodeInstruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!))
|
|
||||||
else if(jump.identifier!=null)
|
|
||||||
VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName)
|
|
||||||
else
|
|
||||||
throw AssemblyError("weird jump")
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateGroup(group: List<PtNode>): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
group.forEach { code += translateNode(it) }
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(assignment: PtAssignment): VmCodeChunk {
|
|
||||||
// TODO can in-place assignments (assignment.augmentable = true) be optimized more?
|
|
||||||
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val resultRegister = vmRegisters.nextFree()
|
|
||||||
code += expressionEval.translateExpression(assignment.value, resultRegister)
|
|
||||||
val ident = assignment.target.identifier
|
|
||||||
val memory = assignment.target.memory
|
|
||||||
val array = assignment.target.array
|
|
||||||
val vmDt = vmType(assignment.value.type)
|
|
||||||
if(ident!=null) {
|
|
||||||
val address = allocations.get(ident.targetName)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
|
|
||||||
}
|
|
||||||
else if(array!=null) {
|
|
||||||
val variable = array.variable.targetName
|
|
||||||
var variableAddr = allocations.get(variable)
|
|
||||||
val itemsize = program.memsizer.memorySize(array.type)
|
|
||||||
val fixedIndex = (array.index as? PtNumber)?.number?.toInt()
|
|
||||||
val vmDtArrayIdx = vmType(array.type)
|
|
||||||
if(fixedIndex!=null) {
|
|
||||||
variableAddr += fixedIndex*itemsize
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)
|
|
||||||
} else {
|
|
||||||
val indexReg = vmRegisters.nextFree()
|
|
||||||
code += expressionEval.translateExpression(array.index, indexReg)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(memory!=null) {
|
|
||||||
if(memory.address is PtNumber) {
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
|
||||||
} else {
|
|
||||||
val addressRegister = vmRegisters.nextFree()
|
|
||||||
code += expressionEval.translateExpression(assignment.value, addressRegister)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw AssemblyError("weird assigntarget")
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(ret: PtReturn): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val value = ret.value
|
|
||||||
if(value!=null) {
|
|
||||||
// Call Convention: return value is always returned in r0
|
|
||||||
code += expressionEval.translateExpression(value, 0)
|
|
||||||
}
|
|
||||||
code += VmCodeInstruction(Opcode.RETURN)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(sub: PtSub): VmCodeChunk {
|
|
||||||
// TODO actually inline subroutines marked as inline
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
|
||||||
code += VmCodeLabel(sub.scopedName)
|
|
||||||
for (child in sub.children) {
|
|
||||||
code += translateNode(child)
|
|
||||||
}
|
|
||||||
code += VmCodeComment("SUB-END '${sub.name}'")
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(block: PtBlock): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
|
||||||
for (child in block.children) {
|
|
||||||
if(child !is PtAssignment) // global variable initialization is done elsewhere
|
|
||||||
code += translateNode(child)
|
|
||||||
}
|
|
||||||
code += VmCodeComment("BLOCK-END '${block.name}'")
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun vmType(type: DataType): VmDataType {
|
|
||||||
return when(type) {
|
|
||||||
DataType.UBYTE,
|
|
||||||
DataType.BYTE -> VmDataType.BYTE
|
|
||||||
DataType.UWORD,
|
|
||||||
DataType.WORD -> VmDataType.WORD
|
|
||||||
in PassByReferenceDatatypes -> VmDataType.WORD
|
|
||||||
else -> throw AssemblyError("no vm datatype for $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var labelSequenceNumber = 0
|
|
||||||
internal fun createLabelName(): List<String> {
|
|
||||||
labelSequenceNumber++
|
|
||||||
return listOf("prog8_label_gen_$labelSequenceNumber")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk =
|
|
||||||
builtinFuncGen.translate(call, resultRegister)
|
|
||||||
}
|
|
@ -1,306 +0,0 @@
|
|||||||
package prog8.codegen.virtual
|
|
||||||
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.StSub
|
|
||||||
import prog8.code.ast.*
|
|
||||||
import prog8.code.core.AssemblyError
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.PassByValueDatatypes
|
|
||||||
import prog8.code.core.SignedDatatypes
|
|
||||||
import prog8.vm.Opcode
|
|
||||||
import prog8.vm.VmDataType
|
|
||||||
|
|
||||||
|
|
||||||
internal class ExpressionGen(private val codeGen: CodeGen) {
|
|
||||||
fun translateExpression(expr: PtExpression, resultRegister: Int): VmCodeChunk {
|
|
||||||
require(codeGen.vmRegisters.peekNext() > resultRegister)
|
|
||||||
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val vmDt = codeGen.vmType(expr.type)
|
|
||||||
|
|
||||||
when (expr) {
|
|
||||||
is PtNumber -> {
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
|
|
||||||
}
|
|
||||||
is PtIdentifier -> {
|
|
||||||
val mem = codeGen.allocations.get(expr.targetName)
|
|
||||||
code += if(expr.type in PassByValueDatatypes) {
|
|
||||||
VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)
|
|
||||||
} else {
|
|
||||||
// for strings and arrays etc., load the *address* of the value instead
|
|
||||||
VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PtAddressOf -> {
|
|
||||||
val mem = codeGen.allocations.get(expr.identifier.targetName)
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
|
|
||||||
}
|
|
||||||
is PtMemoryByte -> {
|
|
||||||
val addressRegister = codeGen.vmRegisters.nextFree()
|
|
||||||
val addressExprCode = translateExpression(expr.address, addressRegister)
|
|
||||||
code += addressExprCode
|
|
||||||
}
|
|
||||||
is PtTypeCast -> code += translate(expr, resultRegister)
|
|
||||||
is PtPrefix -> code += translate(expr, resultRegister)
|
|
||||||
is PtArrayIndexer -> code += translate(expr, resultRegister)
|
|
||||||
is PtBinaryExpression -> code += translate(expr, resultRegister)
|
|
||||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
|
||||||
is PtFunctionCall -> code += translate(expr, resultRegister)
|
|
||||||
is PtContainmentCheck -> code += translate(expr, resultRegister)
|
|
||||||
is PtPipe -> code += translate(expr, resultRegister)
|
|
||||||
is PtRange,
|
|
||||||
is PtArray,
|
|
||||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
|
||||||
else -> throw AssemblyError("weird expression")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
|
|
||||||
TODO("Not yet implemented: pipe expression")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += translateExpression(check.element, resultRegister) // load the element to check in resultRegister
|
|
||||||
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.targetName) as StStaticVariable
|
|
||||||
when(iterable.dt) {
|
|
||||||
DataType.STR -> {
|
|
||||||
val call = PtFunctionCall(listOf("prog8_lib", "string_contains"), false, DataType.UBYTE, check.position)
|
|
||||||
call.children.add(check.element)
|
|
||||||
call.children.add(check.iterable)
|
|
||||||
code += translate(call, resultRegister)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
|
||||||
val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position)
|
|
||||||
call.children.add(check.element)
|
|
||||||
call.children.add(check.iterable)
|
|
||||||
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
|
|
||||||
code += translate(call, resultRegister)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position)
|
|
||||||
call.children.add(check.element)
|
|
||||||
call.children.add(check.iterable)
|
|
||||||
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
|
|
||||||
code += translate(call, resultRegister)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> TODO("containment check in float-array")
|
|
||||||
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int): VmCodeChunk {
|
|
||||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
|
|
||||||
val vmDt = codeGen.vmType(arrayIx.type)
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val idxReg = codeGen.vmRegisters.nextFree()
|
|
||||||
// TODO: optimized code when the index is a constant value
|
|
||||||
code += translateExpression(arrayIx.index, idxReg)
|
|
||||||
if(eltSize>1) {
|
|
||||||
val factorReg = codeGen.vmRegisters.nextFree()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)
|
|
||||||
code += VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=idxReg, reg3=factorReg)
|
|
||||||
}
|
|
||||||
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
|
||||||
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(expr: PtPrefix, resultRegister: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += translateExpression(expr.value, resultRegister)
|
|
||||||
val vmDt = codeGen.vmType(expr.type)
|
|
||||||
when(expr.operator) {
|
|
||||||
"+" -> { }
|
|
||||||
"-" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
|
|
||||||
}
|
|
||||||
"~" -> {
|
|
||||||
val regMask = codeGen.vmRegisters.nextFree()
|
|
||||||
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, reg2=resultRegister, reg3=regMask)
|
|
||||||
}
|
|
||||||
"not" -> {
|
|
||||||
val label = codeGen.createLabelName()
|
|
||||||
code += VmCodeInstruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)
|
|
||||||
code += VmCodeLabel(label)
|
|
||||||
val regMask = codeGen.vmRegisters.nextFree()
|
|
||||||
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)
|
|
||||||
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird prefix operator")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(cast: PtTypeCast, resultRegister: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
if(cast.type==cast.value.type)
|
|
||||||
return code
|
|
||||||
code += translateExpression(cast.value, resultRegister)
|
|
||||||
when(cast.type) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when(cast.value.type) {
|
|
||||||
DataType.BYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
TODO("float -> ubyte") // float not yet supported
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird cast value type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
when(cast.value.type) {
|
|
||||||
DataType.UBYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
TODO("float -> byte") // float not yet supported
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird cast value type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when(cast.value.type) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
// byte -> uword: sign extend
|
|
||||||
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
// ubyte -> uword: sign extend
|
|
||||||
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)
|
|
||||||
}
|
|
||||||
DataType.WORD -> { }
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
TODO("float -> uword") // float not yet supported
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird cast value type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
when(cast.value.type) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
// byte -> word: sign extend
|
|
||||||
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
// byte -> word: sign extend
|
|
||||||
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)
|
|
||||||
}
|
|
||||||
DataType.UWORD -> { }
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
TODO("float -> word") // float not yet supported
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird cast value type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
TODO("floating point not yet supported")
|
|
||||||
// when(cast.value.type) {
|
|
||||||
// DataType.BYTE -> {
|
|
||||||
// }
|
|
||||||
// DataType.UBYTE -> {
|
|
||||||
// }
|
|
||||||
// DataType.WORD -> {
|
|
||||||
// }
|
|
||||||
// DataType.UWORD -> {
|
|
||||||
// }
|
|
||||||
// else -> throw AssemblyError("weird cast value type")
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird cast type")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int): VmCodeChunk {
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
val leftResultReg = codeGen.vmRegisters.nextFree()
|
|
||||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
|
||||||
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. But only if this would result in a different opcode such as ADD 1 -> INC, MUL 1 -> NOP
|
|
||||||
// actually optimizing the code should not be done here but in a tailored code optimizer step.
|
|
||||||
val leftCode = translateExpression(binExpr.left, leftResultReg)
|
|
||||||
val rightCode = translateExpression(binExpr.right, rightResultReg)
|
|
||||||
code += leftCode
|
|
||||||
code += rightCode
|
|
||||||
val vmDt = codeGen.vmType(binExpr.left.type)
|
|
||||||
val signed = binExpr.left.type in SignedDatatypes
|
|
||||||
when(binExpr.operator) {
|
|
||||||
"+" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"-" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"*" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"/" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"%" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"|", "or" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"&", "and" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"^", "xor" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"<<" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
">>" -> {
|
|
||||||
val opc = if(signed) Opcode.ASR else Opcode.LSR
|
|
||||||
code += VmCodeInstruction(opc, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"==" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"!=" -> {
|
|
||||||
code += VmCodeInstruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"<" -> {
|
|
||||||
val ins = if(signed) Opcode.SLTS else Opcode.SLT
|
|
||||||
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
">" -> {
|
|
||||||
val ins = if(signed) Opcode.SGTS else Opcode.SGT
|
|
||||||
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
"<=" -> {
|
|
||||||
val ins = if(signed) Opcode.SLES else Opcode.SLE
|
|
||||||
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
">=" -> {
|
|
||||||
val ins = if(signed) Opcode.SGES else Opcode.SGE
|
|
||||||
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
fun translate(fcall: PtFunctionCall, resultRegister: Int): VmCodeChunk {
|
|
||||||
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
|
|
||||||
val argReg = codeGen.vmRegisters.nextFree()
|
|
||||||
code += translateExpression(arg, argReg)
|
|
||||||
val vmDt = codeGen.vmType(parameter.type)
|
|
||||||
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
|
||||||
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem)
|
|
||||||
}
|
|
||||||
code += VmCodeInstruction(Opcode.CALL, symbol=fcall.functionName)
|
|
||||||
if(!fcall.void && resultRegister!=0) {
|
|
||||||
// Call convention: result value is in r0, so put it in the required register instead.
|
|
||||||
code += VmCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0)
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
package prog8.codegen.virtual
|
|
||||||
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.ast.PtProgram
|
|
||||||
import prog8.code.core.*
|
|
||||||
|
|
||||||
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram, errors: IErrorReporter) {
|
|
||||||
|
|
||||||
private val allocations = mutableMapOf<List<String>, Int>()
|
|
||||||
private var freeMemoryStart: Int
|
|
||||||
|
|
||||||
val freeMem: Int
|
|
||||||
get() = freeMemoryStart
|
|
||||||
|
|
||||||
init {
|
|
||||||
var nextLocation = 0
|
|
||||||
for (variable in st.allVariables) {
|
|
||||||
val memsize =
|
|
||||||
when (variable.dt) {
|
|
||||||
DataType.STR -> variable.initialStringValue!!.first.length + 1 // include the zero byte
|
|
||||||
in NumericDatatypes -> program.memsizer.memorySize(variable.dt)
|
|
||||||
in ArrayDatatypes -> program.memsizer.memorySize(variable.dt, variable.length!!)
|
|
||||||
else -> throw InternalCompilerException("weird dt")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocations[variable.scopedName] = nextLocation
|
|
||||||
nextLocation += memsize
|
|
||||||
}
|
|
||||||
|
|
||||||
freeMemoryStart = nextLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(name: List<String>) = allocations.getValue(name)
|
|
||||||
|
|
||||||
fun asVmMemory(): List<Pair<List<String>, String>> {
|
|
||||||
val mm = mutableListOf<Pair<List<String>, String>>()
|
|
||||||
for (variable in st.allVariables) {
|
|
||||||
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 -> (variable.initialNumericValue ?: 0.0).toString()
|
|
||||||
in NumericDatatypes -> (variable.initialNumericValue ?: 0).toHex()
|
|
||||||
DataType.STR -> {
|
|
||||||
val encoded = program.encoding.encodeString(variable.initialStringValue!!.first, variable.initialStringValue!!.second)
|
|
||||||
encoded.joinToString(",") { it.toInt().toHex() } + ",0"
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
if(variable.initialArrayValue!=null) {
|
|
||||||
variable.initialArrayValue!!.joinToString(",") { it.number!!.toString() }
|
|
||||||
} else {
|
|
||||||
(1..variable.length!!).joinToString(",") { "0" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in ArrayDatatypes -> {
|
|
||||||
if(variable.initialArrayValue!==null) {
|
|
||||||
variable.initialArrayValue!!.joinToString(",") { it.number!!.toHex() }
|
|
||||||
} else {
|
|
||||||
(1..variable.length!!).joinToString(",") { "0" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw InternalCompilerException("weird dt")
|
|
||||||
}
|
|
||||||
mm.add(Pair(variable.scopedName, "${location.toHex()} $typeStr $value"))
|
|
||||||
}
|
|
||||||
return mm
|
|
||||||
}
|
|
||||||
|
|
||||||
private val memorySlabsInternal = mutableMapOf<String, Triple<UInt, UInt, UInt>>()
|
|
||||||
internal val memorySlabs: Map<String, Triple<UInt, UInt, UInt>> = memorySlabsInternal
|
|
||||||
|
|
||||||
fun allocateMemorySlab(name: String, size: UInt, align: UInt): UInt {
|
|
||||||
val address =
|
|
||||||
if(align==0u || align==1u)
|
|
||||||
freeMemoryStart.toUInt()
|
|
||||||
else
|
|
||||||
(freeMemoryStart.toUInt() + align-1u) and (0xffffffffu xor (align-1u))
|
|
||||||
|
|
||||||
memorySlabsInternal[name] = Triple(address, size, align)
|
|
||||||
freeMemoryStart = (address + size).toInt()
|
|
||||||
return address
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMemorySlab(name: String): Triple<UInt, UInt, UInt>? = memorySlabsInternal[name]
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package prog8.optimizer
|
|||||||
import prog8.ast.IStatementContainer
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.AugmentAssignmentOperators
|
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.TypecastExpression
|
import prog8.ast.expressions.TypecastExpression
|
||||||
@ -13,6 +12,7 @@ import prog8.ast.statements.Assignment
|
|||||||
import prog8.ast.statements.AssignmentOrigin
|
import prog8.ast.statements.AssignmentOrigin
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.code.core.AugmentAssignmentOperators
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
@ -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
|
||||||
|
|
||||||
@ -97,7 +100,8 @@ X = BinExpr X = LeftExpr
|
|||||||
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
||||||
// (that has the type of the expression), and then finally doing the typecast.
|
// (that has the type of the expression), and then finally doing the typecast.
|
||||||
// Once it's outside the typecast, the regular splitting can commence.
|
// Once it's outside the typecast, the regular splitting can commence.
|
||||||
val (tempVarName, _) = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED))
|
val tempvarDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
|
val (tempVarName, _) = program.getTempVar(tempvarDt)
|
||||||
val assignTempVar = Assignment(
|
val assignTempVar = Assignment(
|
||||||
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
|
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
|
||||||
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
|
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
|
||||||
|
@ -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,17 +2,17 @@ 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
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
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() {
|
||||||
@ -34,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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -107,9 +56,32 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
* (X + c1) - c2 -> X + (c1-c2)
|
* (X + c1) - c2 -> X + (c1-c2)
|
||||||
*/
|
*/
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
val rightconst = expr.right.constValue(program)
|
val rightconst = expr.right.constValue(program)
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
|
if(expr.left.inferType(program) istype DataType.STR) {
|
||||||
|
if(expr.operator=="+" && expr.left is StringLiteral && expr.right is StringLiteral) {
|
||||||
|
// concatenate two strings.
|
||||||
|
val leftString = expr.left as StringLiteral
|
||||||
|
val rightString = expr.right as StringLiteral
|
||||||
|
val concatenated = if(leftString.encoding==rightString.encoding) {
|
||||||
|
leftString.value + rightString.value
|
||||||
|
} else {
|
||||||
|
program.encoding.decodeString(
|
||||||
|
program.encoding.encodeString(leftString.value, leftString.encoding) + program.encoding.encodeString(rightString.value, rightString.encoding),
|
||||||
|
leftString.encoding)
|
||||||
|
}
|
||||||
|
val concatStr = StringLiteral(concatenated, leftString.encoding, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, concatStr, parent))
|
||||||
|
}
|
||||||
|
else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) {
|
||||||
|
// mutiply a string.
|
||||||
|
val part = expr.left as StringLiteral
|
||||||
|
val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newStr, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(expr.operator=="==" && rightconst!=null) {
|
if(expr.operator=="==" && rightconst!=null) {
|
||||||
val leftExpr = expr.left as? BinaryExpression
|
val leftExpr = expr.left as? BinaryExpression
|
||||||
@ -168,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
|
||||||
@ -278,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))
|
||||||
@ -286,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)
|
||||||
@ -399,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,22 +1,28 @@
|
|||||||
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.statements.*
|
import prog8.ast.maySwapOperandOrder
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.IfElse
|
||||||
|
import prog8.ast.statements.Jump
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.target.VMTarget
|
||||||
import prog8.code.core.IntegerDatatypes
|
|
||||||
import prog8.code.core.NumericDatatypes
|
|
||||||
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 errors: IErrorReporter,
|
||||||
|
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()
|
||||||
|
|
||||||
@ -27,8 +33,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
|
||||||
@ -70,6 +77,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +106,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +193,15 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expr.operator == ">=" && rightVal?.number == 0.0) {
|
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||||
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
if(expr.operator == ">=" && rightVal?.number == 0.0) {
|
||||||
// unsigned >= 0 --> true
|
// unsigned >= 0 --> true
|
||||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(true, expr.position), parent))
|
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(true, expr.position), parent))
|
||||||
}
|
}
|
||||||
|
else if(expr.operator == ">" && rightVal?.number == 0.0) {
|
||||||
|
// unsigned > 0 --> unsigned != 0
|
||||||
|
return listOf(IAstModification.SetExpression({expr.operator="!="}, expr, parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftDt!=DataType.FLOAT && expr.operator == "<" && rightVal?.number == 1.0) {
|
if(leftDt!=DataType.FLOAT && expr.operator == "<" && rightVal?.number == 1.0) {
|
||||||
@ -183,42 +210,42 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expr.operator == "<" && rightVal?.number == 0.0) {
|
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||||
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
if(expr.operator == "<" && rightVal?.number == 0.0) {
|
||||||
// unsigned < 0 --> false
|
// unsigned < 0 --> false
|
||||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
||||||
}
|
}
|
||||||
|
else if(expr.operator == "<=" && rightVal?.number == 0.0) {
|
||||||
|
// unsigned <= 0 --> unsigned==0
|
||||||
|
return listOf(IAstModification.SetExpression({expr.operator="=="}, expr, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(leftDt==DataType.BOOL) {
|
||||||
|
// optimize boolean constant comparisons
|
||||||
|
// if(expr.operator=="==" && rightVal?.number==0.0)
|
||||||
|
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||||
|
// if(expr.operator=="!=" && rightVal?.number==1.0)
|
||||||
|
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||||
|
if(expr.operator=="==" && rightVal?.number==1.0)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||||
|
if(expr.operator=="!=" && rightVal?.number==0.0)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, expr.left, 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
|
||||||
@ -262,6 +289,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))
|
||||||
|
|
||||||
@ -270,6 +319,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
|
|
||||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
|
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
|
||||||
|
if(functionCallExpr.args.isEmpty())
|
||||||
|
return noModifications
|
||||||
val arg = functionCallExpr.args[0]
|
val arg = functionCallExpr.args[0]
|
||||||
if(arg is TypecastExpression) {
|
if(arg is TypecastExpression) {
|
||||||
val valueDt = arg.expression.inferType(program)
|
val valueDt = arg.expression.inferType(program)
|
||||||
@ -278,6 +329,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
|
||||||
@ -286,6 +343,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
|
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
|
||||||
|
if(functionCallExpr.args.isEmpty())
|
||||||
|
return noModifications
|
||||||
val arg = functionCallExpr.args[0]
|
val arg = functionCallExpr.args[0]
|
||||||
if(arg is TypecastExpression) {
|
if(arg is TypecastExpression) {
|
||||||
val valueDt = arg.expression.inferType(program)
|
val valueDt = arg.expression.inferType(program)
|
||||||
@ -297,6 +356,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
|
||||||
@ -308,49 +373,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
|
||||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||||
val range = containment.iterable as? RangeExpression
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,19 +516,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) {
|
||||||
@ -556,9 +581,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,18 +608,46 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
|
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
|
errors.warn("shift always results in 0", expr.position)
|
||||||
return NumericLiteral(targetDt, 0.0, expr.position)
|
return NumericLiteral(targetDt, 0.0, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD -> {
|
||||||
if (amount >= 16) {
|
if (amount >= 16) {
|
||||||
|
errors.warn("shift always results in 0", expr.position)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if (amount >= 16) {
|
||||||
|
errors.warn("shift always results in 0", expr.position)
|
||||||
|
return NumericLiteral(targetDt, 0.0, expr.position)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||||
|
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||||
|
}
|
||||||
|
else if (amount > 8) {
|
||||||
|
// same as above but with residual shifts.
|
||||||
|
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
|
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||||
|
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,6 +668,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
when (idt.getOr(DataType.UNDEFINED)) {
|
when (idt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
|
errors.warn("shift always results in 0", expr.position)
|
||||||
return NumericLiteral.optimalInteger(0, expr.position)
|
return NumericLiteral.optimalInteger(0, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,14 +680,22 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if (amount >= 16) {
|
if (amount >= 16) {
|
||||||
|
errors.warn("shift always results in 0", expr.position)
|
||||||
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
|
||||||
@ -645,7 +708,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
|
||||||
|
@ -54,8 +54,14 @@ fun Program.optimizeStatements(errors: IErrorReporter,
|
|||||||
return optimizationCount
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
fun Program.inlineSubroutines(): Int {
|
||||||
val opti = ExpressionSimplifier(this, errors)
|
val inliner = Inliner(this)
|
||||||
|
inliner.visit(this)
|
||||||
|
return inliner.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarget) : Int {
|
||||||
|
val opti = ExpressionSimplifier(this, errors, target)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
238
codeOptimizers/src/prog8/optimizer/Inliner.kt
Normal file
238
codeOptimizers/src/prog8/optimizer/Inliner.kt
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.ast.walk.AstWalker
|
||||||
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
|
||||||
|
|
||||||
|
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 DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||||
|
private val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
visit(program)
|
||||||
|
modifications.forEach { it.perform() }
|
||||||
|
modifications.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(subroutine: Subroutine) {
|
||||||
|
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
|
||||||
|
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
|
||||||
|
if(!containsSubsOrVariables) {
|
||||||
|
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))) {
|
||||||
|
// subroutine is possible candidate to be inlined
|
||||||
|
subroutine.inline =
|
||||||
|
when(val stmt=subroutine.statements[0]) {
|
||||||
|
is Return -> {
|
||||||
|
if(stmt.value is NumericLiteral)
|
||||||
|
true
|
||||||
|
else if(stmt.value==null)
|
||||||
|
true
|
||||||
|
else if (stmt.value is IdentifierReference) {
|
||||||
|
makeFullyScoped(stmt.value as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else if(stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size<=1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||||
|
when (stmt.value) {
|
||||||
|
is BuiltinFunctionCall -> {
|
||||||
|
makeFullyScoped(stmt.value as BuiltinFunctionCall)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
is FunctionCallExpression -> {
|
||||||
|
makeFullyScoped(stmt.value as FunctionCallExpression)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
is Assignment -> {
|
||||||
|
if(stmt.value.isSimple) {
|
||||||
|
val targetInline =
|
||||||
|
if(stmt.target.identifier!=null) {
|
||||||
|
makeFullyScoped(stmt.target.identifier!!)
|
||||||
|
true
|
||||||
|
} else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||||
|
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
val valueInline =
|
||||||
|
if(stmt.value is IdentifierReference) {
|
||||||
|
makeFullyScoped(stmt.value as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else if((stmt.value as? DirectMemoryRead)?.addressExpression is NumericLiteral || (stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference) {
|
||||||
|
if((stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped((stmt.value as? DirectMemoryRead)?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
targetInline || valueInline
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
is BuiltinFunctionCallStatement -> {
|
||||||
|
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||||
|
if(inline)
|
||||||
|
makeFullyScoped(stmt)
|
||||||
|
inline
|
||||||
|
}
|
||||||
|
is FunctionCallStatement -> {
|
||||||
|
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||||
|
if(inline)
|
||||||
|
makeFullyScoped(stmt)
|
||||||
|
inline
|
||||||
|
}
|
||||||
|
is PostIncrDecr -> {
|
||||||
|
if(stmt.target.identifier!=null) {
|
||||||
|
makeFullyScoped(stmt.target.identifier!!)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||||
|
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
is Jump -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visit(subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||||
|
val scoped = (identifier.targetStatement(program)!! as INamedStatement).scopedName
|
||||||
|
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFullyScoped(call: BuiltinFunctionCall) {
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeScopedArgs(args: List<Expression>): List<Expression> {
|
||||||
|
return args.map {
|
||||||
|
when (it) {
|
||||||
|
is NumericLiteral -> it.copy()
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val scoped = (it.targetStatement(program)!! as INamedStatement).scopedName
|
||||||
|
IdentifierReference(scoped, it.position)
|
||||||
|
}
|
||||||
|
else -> throw InternalCompilerException("expected only number or identifier arg, otherwise too complex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(program: Program): Iterable<IAstModification> {
|
||||||
|
DetermineInlineSubs(program)
|
||||||
|
return super.before(program)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(sub.inline && sub.parameters.isEmpty()) {
|
||||||
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||||
|
"invalid inline sub at ${sub.position}"
|
||||||
|
}
|
||||||
|
return if(sub.isAsmSubroutine) {
|
||||||
|
// simply insert the asm for the argument-less routine
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
||||||
|
} else {
|
||||||
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
|
when (val toInline = sub.statements.first()) {
|
||||||
|
is Return -> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
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()) {
|
||||||
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||||
|
"invalid inline sub at ${sub.position}"
|
||||||
|
}
|
||||||
|
return if(sub.isAsmSubroutine) {
|
||||||
|
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
|
||||||
|
noModifications
|
||||||
|
} else {
|
||||||
|
when (val toInline = sub.statements.first()) {
|
||||||
|
is Return -> {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ 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 prog8.code.target.VMTarget
|
||||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
@ -17,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.
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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]
|
||||||
@ -114,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,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(
|
||||||
@ -163,11 +97,13 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only if-part
|
// always true -> keep only if-part
|
||||||
errors.warn("condition is always true", ifElse.condition.position)
|
if(!ifElse.definingModule.isLibrary)
|
||||||
|
errors.warn("condition is always true", ifElse.condition.position)
|
||||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
|
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
|
||||||
} else {
|
} else {
|
||||||
// always false -> keep only else-part
|
// always false -> keep only else-part
|
||||||
errors.warn("condition is always false", ifElse.condition.position)
|
if(!ifElse.definingModule.isLibrary)
|
||||||
|
errors.warn("condition is always false", ifElse.condition.position)
|
||||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
|
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,25 +221,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
|
||||||
@ -314,7 +231,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))
|
||||||
}
|
}
|
||||||
@ -353,7 +270,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,9 +293,9 @@ class StatementOptimizer(private val program: Program,
|
|||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val rightCv = bexpr.right.constValue(program)?.number
|
val rightCv = bexpr.right.constValue(program)?.number
|
||||||
if(bexpr.operator=="-" && rightCv==null) {
|
if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
|
||||||
if(bexpr.right isSameAs assignment.target) {
|
if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
|
||||||
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation)
|
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
|
||||||
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
|
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
|
||||||
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
|
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
@ -440,64 +357,21 @@ class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// word = msb(word) , word=lsb(word)
|
// word = lsb(word)
|
||||||
if(assignment.target.inferType(program).isWords) {
|
if(assignment.target.inferType(program).isWords) {
|
||||||
var fcall = assignment.value as? FunctionCallExpression
|
var fcall = assignment.value as? FunctionCallExpression
|
||||||
if (fcall == null)
|
if (fcall == null)
|
||||||
fcall = (assignment.value as? TypecastExpression)?.expression as? FunctionCallExpression
|
fcall = (assignment.value as? TypecastExpression)?.expression as? FunctionCallExpression
|
||||||
if (fcall != null && (fcall.target.nameInSource == listOf("lsb") || fcall.target.nameInSource == listOf("msb"))) {
|
if (fcall != null && (fcall.target.nameInSource == listOf("lsb"))) {
|
||||||
if (fcall.args.single() isSameAs assignment.target) {
|
if (fcall.args.single() isSameAs assignment.target) {
|
||||||
return if (fcall.target.nameInSource == listOf("lsb")) {
|
// optimize word=lsb(word) ==> word &= $00ff
|
||||||
// optimize word=lsb(word) ==> word &= $00ff
|
val and255 = BinaryExpression(fcall.args[0], "&", NumericLiteral(DataType.UWORD, 255.0, fcall.position), fcall.position)
|
||||||
val and255 = BinaryExpression(fcall.args[0], "&", NumericLiteral(DataType.UWORD, 255.0, fcall.position), fcall.position)
|
val newAssign = Assignment(assignment.target, and255, AssignmentOrigin.OPTIMIZER, fcall.position)
|
||||||
val newAssign = Assignment(assignment.target, and255, AssignmentOrigin.OPTIMIZER, fcall.position)
|
return listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
|
||||||
listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
|
|
||||||
} else {
|
|
||||||
// optimize word=msb(word) ==> word >>= 8
|
|
||||||
val shift8 = BinaryExpression(fcall.args[0], ">>", NumericLiteral(DataType.UBYTE, 8.0, fcall.position), fcall.position)
|
|
||||||
val newAssign = Assignment(assignment.target, shift8, AssignmentOrigin.OPTIMIZER, fcall.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
|
||||||
|
|
||||||
if(compTarget.name==VMTarget.NAME)
|
|
||||||
return noModifications
|
|
||||||
|
|
||||||
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
|
||||||
val subr = returnStmt.definingSubroutine!!
|
|
||||||
val returnDt = subr.returntypes.single()
|
|
||||||
if (returnDt in IntegerDatatypes) {
|
|
||||||
// first assign to intermediary variable, then return that
|
|
||||||
val (returnVarName, _) = program.getTempVar(returnDt)
|
|
||||||
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
|
||||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
|
||||||
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
|
||||||
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
@ -117,12 +120,26 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if(singleUse is AssignTarget) {
|
if(singleUse is AssignTarget) {
|
||||||
val assignment = singleUse.parent as Assignment
|
val assignment = singleUse.parent as Assignment
|
||||||
if(assignment.origin==AssignmentOrigin.VARINIT) {
|
if(assignment.origin==AssignmentOrigin.VARINIT) {
|
||||||
if(!decl.definingModule.isLibrary)
|
if(assignment.value.isSimple) {
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
// remove the vardecl
|
||||||
return listOf(
|
if(!decl.definingModule.isLibrary)
|
||||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
return listOf(
|
||||||
)
|
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||||
|
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
} else if(assignment.value is IFunctionCall) {
|
||||||
|
// replace the unused variable's initializer function call by a void
|
||||||
|
errors.warn("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||||
|
val fcall = assignment.value as IFunctionCall
|
||||||
|
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||||
|
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
errors.warn("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +253,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)
|
||||||
|
@ -27,20 +27,21 @@ compileTestKotlin {
|
|||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':codeAst')
|
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
implementation project(':codeOptimizers')
|
implementation project(':codeOptimizers')
|
||||||
implementation project(':compilerAst')
|
implementation project(':compilerAst')
|
||||||
implementation project(':codeGenCpu6502')
|
implementation project(':codeGenCpu6502')
|
||||||
implementation project(':codeGenVirtual')
|
implementation project(':codeGenIntermediate')
|
||||||
implementation project(':codeGenExperimental')
|
implementation project(':codeGenExperimental')
|
||||||
implementation 'org.antlr:antlr4-runtime:4.9.3'
|
implementation project(':virtualmachine')
|
||||||
|
implementation 'org.antlr:antlr4-runtime:4.11.1'
|
||||||
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 project(':intermediate')
|
||||||
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
@ -68,7 +69,6 @@ sourceSets {
|
|||||||
test {
|
test {
|
||||||
java {
|
java {
|
||||||
srcDir "${project.projectDir}/test"
|
srcDir "${project.projectDir}/test"
|
||||||
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="codeAst" />
|
|
||||||
<orderEntry type="module" module-name="compilerAst" />
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
<orderEntry type="module" module-name="codeOptimizers" />
|
<orderEntry type="module" module-name="codeOptimizers" />
|
||||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||||
<orderEntry type="module" module-name="codeGenVirtual" />
|
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||||
|
<orderEntry type="module" module-name="virtualmachine" />
|
||||||
|
<orderEntry type="module" module-name="intermediate" scope="TEST" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -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 {
|
||||||
|
|
||||||
@ -67,6 +64,17 @@ sys {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||||
|
; Called when the compiler wants to assign a string value to another string.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jmp prog8_lib.strcpy
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
; note: only works for NON-OVERLAPPING memory regions!
|
; note: only works for NON-OVERLAPPING memory regions!
|
||||||
; note: can't be inlined because is called from asm as well
|
; note: can't be inlined because is called from asm as well
|
||||||
|
@ -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
|
||||||
|
@ -116,8 +116,8 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
|||||||
; -- cast fac1 to word into A/Y
|
; -- cast fac1 to word into A/Y
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldy $66
|
ldy floats.AYINT_facmo
|
||||||
lda $67
|
lda floats.AYINT_facmo+1
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -168,9 +168,9 @@ stack_float2w .proc ; also used for float2b
|
|||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda $66
|
lda floats.AYINT_facmo
|
||||||
sta P8ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
lda $67
|
lda floats.AYINT_facmo+1
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
; 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
|
||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-128 compatible floating point related functions ----
|
; ---- this block contains C-128 compatible floating point related functions ----
|
||||||
@ -57,7 +56,8 @@ romsub $af4b = ROUND() clobbers(A,X,Y) ; round fac1
|
|||||||
romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
||||||
romsub $af51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
romsub $af51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||||
romsub $af54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
romsub $af54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||||
romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: special cx16 setup required, use RND() stub instead!!
|
romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
|
romsub $af57 = RND() clobbers(A,X,Y) ; alias for RND_0
|
||||||
romsub $af5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
romsub $af5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
romsub $af5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2
|
romsub $af5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2
|
||||||
romsub $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
|
romsub $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
|
||||||
@ -110,7 +110,6 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -151,39 +150,14 @@ asmsub FREADUY (ubyte value @Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
&uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT
|
||||||
; ---- prints the floating point value (without a newline).
|
|
||||||
%asm {{
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #<value
|
|
||||||
ldy #>value
|
|
||||||
jsr MOVFM ; load float into fac1
|
|
||||||
jsr FOUT ; fac1 to string in A/Y
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
ldy #0
|
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
|
||||||
beq +
|
|
||||||
jsr c64.CHROUT
|
|
||||||
iny
|
|
||||||
bne -
|
|
||||||
+ ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pow(float value, float power) -> float {
|
sub rndf() -> float {
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
stx P8ZP_SCRATCH_REG
|
||||||
phy
|
lda #1
|
||||||
lda #<value
|
jsr RND_0
|
||||||
ldy #>value
|
ldx P8ZP_SCRATCH_REG
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<power
|
|
||||||
ldy #>power
|
|
||||||
jsr floats.FPWR
|
|
||||||
ply
|
|
||||||
plx
|
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
@ -15,9 +12,34 @@ c64 {
|
|||||||
|
|
||||||
&ubyte COLOR = $00f1 ; cursor color
|
&ubyte COLOR = $00f1 ; cursor color
|
||||||
;;&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) // TODO c128 ??
|
;;&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) // TODO c128 ??
|
||||||
|
|
||||||
|
&uword IERROR = $0300
|
||||||
|
&uword IMAIN = $0302
|
||||||
|
&uword ICRNCH = $0304
|
||||||
|
&uword IQPLOP = $0306
|
||||||
|
&uword IGONE = $0308
|
||||||
|
&uword IEVAL = $030a
|
||||||
|
&uword ICRNCH2 = $030c
|
||||||
|
&uword IQPLOP2 = $030e
|
||||||
|
&uword IGONE2 = $0310
|
||||||
|
; $0312 and $0313 are unused.
|
||||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||||
&uword CBINV = $0316 ; BRK vector (in ram)
|
&uword CBINV = $0316 ; BRK vector (in ram)
|
||||||
&uword NMINV = $0318 ; NMI vector (in ram)
|
&uword NMINV = $0318 ; NMI vector (in ram)
|
||||||
|
&uword IOPEN = $031a
|
||||||
|
&uword ICLOSE = $031c
|
||||||
|
&uword ICHKIN = $031e
|
||||||
|
&uword ICKOUT = $0320
|
||||||
|
&uword ICLRCH = $0322
|
||||||
|
&uword IBASIN = $0324
|
||||||
|
&uword IBSOUT = $0326
|
||||||
|
&uword ISTOP = $0328
|
||||||
|
&uword IGETIN = $032a
|
||||||
|
&uword ICLALL = $032c
|
||||||
|
&uword IEXMON = $032e
|
||||||
|
&uword ILOAD = $0330
|
||||||
|
&uword ISAVE = $0332
|
||||||
|
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
@ -274,6 +296,16 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
lda #0
|
||||||
|
sta 247 ; enable charset switching
|
||||||
|
lda #110
|
||||||
|
sta 808 ; enable run/stop key
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
@ -481,6 +513,13 @@ asmsub init_system_phase2() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub cleanup_at_exit() {
|
||||||
|
; executed when the main subroutine does rts
|
||||||
|
%asm {{
|
||||||
|
jmp c64.enable_runstop_and_charsetswitch
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub disable_basic() clobbers(A) {
|
asmsub disable_basic() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $0a04 ; disable BASIC shadow registers
|
lda $0a04 ; disable BASIC shadow registers
|
||||||
@ -522,15 +561,32 @@ sys {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub wait(uword jiffies) {
|
asmsub wait(uword jiffies @AY) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||||
repeat jiffies {
|
%asm {{
|
||||||
ubyte jiff = lsb(c64.RDTIM16())
|
stx P8ZP_SCRATCH_B1
|
||||||
while jiff==lsb(c64.RDTIM16()) {
|
sta P8ZP_SCRATCH_W1
|
||||||
; wait until 1 jiffy has passed
|
sty P8ZP_SCRATCH_W1+1
|
||||||
}
|
|
||||||
}
|
_loop lda P8ZP_SCRATCH_W1
|
||||||
|
ora P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
ldx P8ZP_SCRATCH_B1
|
||||||
|
rts
|
||||||
|
|
||||||
|
+ lda c64.TIME_LO
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
- lda c64.TIME_LO
|
||||||
|
cmp P8ZP_SCRATCH_B1
|
||||||
|
beq -
|
||||||
|
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
bne +
|
||||||
|
dec P8ZP_SCRATCH_W1+1
|
||||||
|
+ dec P8ZP_SCRATCH_W1
|
||||||
|
jmp _loop
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub waitvsync() clobbers(A) {
|
asmsub waitvsync() clobbers(A) {
|
||||||
@ -554,6 +610,17 @@ sys {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||||
|
; Called when the compiler wants to assign a string value to another string.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jmp prog8_lib.strcpy
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
; note: only works for NON-OVERLAPPING memory regions!
|
; note: only works for NON-OVERLAPPING memory regions!
|
||||||
; note: can't be inlined because is called from asm as well
|
; note: can't be inlined because is called from asm as well
|
||||||
@ -670,7 +737,10 @@ _longcopy
|
|||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
%asm {{
|
%asm {{
|
||||||
|
;lda #14
|
||||||
|
;sta $01 ; bank the kernal in TODO c128 how to do this?
|
||||||
jsr c64.CLRCHN ; reset i/o channels
|
jsr c64.CLRCHN ; reset i/o channels
|
||||||
|
jsr c64.enable_runstop_and_charsetswitch
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
txs
|
txs
|
||||||
rts ; return to original caller
|
rts ; return to original caller
|
||||||
@ -694,6 +764,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
|
||||||
|
@ -113,8 +113,8 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
|||||||
; -- cast fac1 to word into A/Y
|
; -- cast fac1 to word into A/Y
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldy $64
|
ldy floats.AYINT_facmo
|
||||||
lda $65
|
lda floats.AYINT_facmo+1
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -165,9 +165,9 @@ stack_float2w .proc ; also used for float2b
|
|||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda $64
|
lda floats.AYINT_facmo
|
||||||
sta P8ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
lda $65
|
lda floats.AYINT_facmo+1
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
; 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
|
||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
@ -12,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 ----
|
||||||
|
|
||||||
@ -91,7 +87,6 @@ romsub $e2b4 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
|||||||
romsub $e30e = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
romsub $e30e = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
asmsub FREADS32() clobbers(A,X,Y) {
|
asmsub FREADS32() clobbers(A,X,Y) {
|
||||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -174,43 +169,20 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
&uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT
|
||||||
; ---- prints the floating point value (without a newline).
|
|
||||||
%asm {{
|
|
||||||
stx floats_store_reg
|
|
||||||
lda #<value
|
|
||||||
ldy #>value
|
|
||||||
jsr MOVFM ; load float into fac1
|
|
||||||
jsr FOUT ; fac1 to string in A/Y
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
ldy #0
|
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
|
||||||
beq +
|
|
||||||
jsr c64.CHROUT
|
|
||||||
iny
|
|
||||||
bne -
|
|
||||||
+ ldx floats_store_reg
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pow(float value, float power) -> float {
|
sub rndf() -> float {
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
stx P8ZP_SCRATCH_REG
|
||||||
phy
|
lda #1
|
||||||
lda #<value
|
jsr FREADSA
|
||||||
ldy #>value
|
jsr RND ; rng into fac1
|
||||||
jsr floats.CONUPK
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda #<power
|
|
||||||
ldy #>power
|
|
||||||
jsr floats.FPWR
|
|
||||||
ply
|
|
||||||
plx
|
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
%asminclude "library:c64/floats.asm"
|
%asminclude "library:c64/floats.asm"
|
||||||
%asminclude "library:c64/floats_funcs.asm"
|
%asminclude "library:c64/floats_funcs.asm"
|
||||||
|
|
||||||
|
@ -1,188 +1,6 @@
|
|||||||
; --- floating point builtin functions
|
; --- floating point builtin functions
|
||||||
|
|
||||||
|
|
||||||
abs_f_stack .proc
|
|
||||||
; -- push abs(AY) on stack
|
|
||||||
jsr floats.MOVFM
|
|
||||||
jsr floats.ABS
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
abs_f_fac1 .proc
|
|
||||||
; -- FAC1 = abs(AY)
|
|
||||||
jsr floats.MOVFM
|
|
||||||
jmp floats.ABS
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_atan_stack .proc
|
|
||||||
jsr func_atan_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_atan_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr ATN
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ceil_stack .proc
|
|
||||||
jsr func_ceil_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ceil_fac1 .proc
|
|
||||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
ldx #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr MOVMF
|
|
||||||
jsr INT
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FCOMP
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
lda #<FL_ONE_const
|
|
||||||
ldy #>FL_ONE_const
|
|
||||||
jsr FADD
|
|
||||||
+ ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_floor_stack .proc
|
|
||||||
jsr func_floor_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_floor_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr INT
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_round_stack .proc
|
|
||||||
jsr func_round_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_round_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr FADDH
|
|
||||||
jsr INT
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sin_stack .proc
|
|
||||||
jsr func_sin_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sin_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr SIN
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_cos_stack .proc
|
|
||||||
jsr func_cos_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_cos_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr COS
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_tan_stack .proc
|
|
||||||
jsr func_tan_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_tan_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr TAN
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rad_stack .proc
|
|
||||||
jsr func_rad_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rad_fac1 .proc
|
|
||||||
; -- convert degrees to radians (d * pi / 180)
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #<_pi_div_180
|
|
||||||
ldy #>_pi_div_180
|
|
||||||
jsr FMULT
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_deg_stack .proc
|
|
||||||
jsr func_deg_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_deg_fac1 .proc
|
|
||||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #<_one_over_pi_div_180
|
|
||||||
ldy #>_one_over_pi_div_180
|
|
||||||
jsr FMULT
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ln_stack .proc
|
|
||||||
jsr func_ln_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ln_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr LOG
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_log2_stack .proc
|
|
||||||
jsr func_log2_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_log2_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr LOG
|
|
||||||
jsr MOVEF
|
|
||||||
lda #<FL_LOG2_const
|
|
||||||
ldy #>FL_LOG2_const
|
|
||||||
jsr MOVFM
|
|
||||||
jsr FDIVT
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sign_f_stack .proc
|
func_sign_f_stack .proc
|
||||||
jsr func_sign_f_into_A
|
jsr func_sign_f_into_A
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
@ -195,33 +13,6 @@ func_sign_f_into_A .proc
|
|||||||
jmp SIGN
|
jmp SIGN
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_sqrt_stack .proc
|
|
||||||
jsr func_sqrt_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sqrt_fac1 .proc
|
|
||||||
jsr MOVFM
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr SQR
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rndf_stack .proc
|
|
||||||
jsr func_rndf_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rndf_fac1 .proc
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #1
|
|
||||||
jsr FREADSA
|
|
||||||
jsr RND ; rng into fac1
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_swap_f .proc
|
func_swap_f .proc
|
||||||
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||||
ldy #4
|
ldy #4
|
||||||
@ -353,85 +144,3 @@ func_all_f_stack .proc
|
|||||||
jsr a_times_5
|
jsr a_times_5
|
||||||
jmp prog8_lib.func_all_b_stack
|
jmp prog8_lib.func_all_b_stack
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_max_f_stack .proc
|
|
||||||
jsr func_max_f_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_max_f_fac1 .proc
|
|
||||||
; -- max(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
|
||||||
_loop_count = P8ZP_SCRATCH_REG
|
|
||||||
stx floats_store_reg
|
|
||||||
sta _loop_count
|
|
||||||
lda #255
|
|
||||||
sta _minmax_cmp+1 ; modifying
|
|
||||||
lda #<_largest_neg_float
|
|
||||||
ldy #>_largest_neg_float
|
|
||||||
_minmax_entry jsr MOVFM
|
|
||||||
- lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
jsr FCOMP
|
|
||||||
_minmax_cmp cmp #255 ; modified
|
|
||||||
bne +
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
jsr MOVFM
|
|
||||||
+ lda P8ZP_SCRATCH_W1
|
|
||||||
clc
|
|
||||||
adc #5
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
+ dec _loop_count
|
|
||||||
bne -
|
|
||||||
ldx floats_store_reg
|
|
||||||
rts
|
|
||||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_min_f_stack .proc
|
|
||||||
jsr func_min_f_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_min_f_fac1 .proc
|
|
||||||
; -- min(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
|
||||||
sta func_max_f_fac1._loop_count
|
|
||||||
lda #1
|
|
||||||
sta func_max_f_fac1._minmax_cmp+1
|
|
||||||
lda #<_largest_pos_float
|
|
||||||
ldy #>_largest_pos_float
|
|
||||||
jmp func_max_f_fac1._minmax_entry
|
|
||||||
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
func_sum_f_stack .proc
|
|
||||||
jsr func_sum_f_fac1
|
|
||||||
jmp push_fac1
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sum_f_fac1 .proc
|
|
||||||
; -- sum(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
|
||||||
_loop_count = P8ZP_SCRATCH_REG
|
|
||||||
stx floats_store_reg
|
|
||||||
sta _loop_count
|
|
||||||
lda #<FL_ZERO_const
|
|
||||||
ldy #>FL_ZERO_const
|
|
||||||
jsr MOVFM
|
|
||||||
- lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
jsr FADD
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
clc
|
|
||||||
adc #5
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
+ dec _loop_count
|
|
||||||
bne -
|
|
||||||
ldx floats_store_reg
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
@ -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
|
||||||
@ -15,9 +11,36 @@ c64 {
|
|||||||
|
|
||||||
&ubyte COLOR = $0286 ; cursor color
|
&ubyte COLOR = $0286 ; cursor color
|
||||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||||
|
|
||||||
|
&uword IERROR = $0300
|
||||||
|
&uword IMAIN = $0302
|
||||||
|
&uword ICRNCH = $0304
|
||||||
|
&uword IQPLOP = $0306
|
||||||
|
&uword IGONE = $0308
|
||||||
|
&uword IEVAL = $030a
|
||||||
|
&ubyte SAREG = $030c ; register storage for A for SYS calls
|
||||||
|
&ubyte SXREG = $030d ; register storage for X for SYS calls
|
||||||
|
&ubyte SYREG = $030e ; register storage for Y for SYS calls
|
||||||
|
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
|
||||||
|
&uword USRADD = $0311 ; vector for the USR() basic command
|
||||||
|
; $0313 is unused.
|
||||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||||
&uword CBINV = $0316 ; BRK vector (in ram)
|
&uword CBINV = $0316 ; BRK vector (in ram)
|
||||||
&uword NMINV = $0318 ; NMI vector (in ram)
|
&uword NMINV = $0318 ; NMI vector (in ram)
|
||||||
|
&uword IOPEN = $031a
|
||||||
|
&uword ICLOSE = $031c
|
||||||
|
&uword ICHKIN = $031e
|
||||||
|
&uword ICKOUT = $0320
|
||||||
|
&uword ICLRCH = $0322
|
||||||
|
&uword IBASIN = $0324
|
||||||
|
&uword IBSOUT = $0326
|
||||||
|
&uword ISTOP = $0328
|
||||||
|
&uword IGETIN = $032a
|
||||||
|
&uword ICLALL = $032c
|
||||||
|
&uword USERCMD = $032e
|
||||||
|
&uword ILOAD = $0330
|
||||||
|
&uword ISAVE = $0332
|
||||||
|
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
@ -300,6 +323,13 @@ asmsub init_system_phase2() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub cleanup_at_exit() {
|
||||||
|
; executed when the main subroutine does rts
|
||||||
|
%asm {{
|
||||||
|
jmp c64.enable_runstop_and_charsetswitch
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #$80
|
lda #$80
|
||||||
@ -310,6 +340,16 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
lda #0
|
||||||
|
sta 657 ; enable charset switching
|
||||||
|
lda #237
|
||||||
|
sta 808 ; enable run/stop key
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
@ -487,15 +527,32 @@ sys {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub wait(uword jiffies) {
|
asmsub wait(uword jiffies @AY) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||||
repeat jiffies {
|
%asm {{
|
||||||
ubyte jiff = lsb(c64.RDTIM16())
|
stx P8ZP_SCRATCH_B1
|
||||||
while jiff==lsb(c64.RDTIM16()) {
|
sta P8ZP_SCRATCH_W1
|
||||||
; wait until 1 jiffy has passed
|
sty P8ZP_SCRATCH_W1+1
|
||||||
}
|
|
||||||
}
|
_loop lda P8ZP_SCRATCH_W1
|
||||||
|
ora P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
ldx P8ZP_SCRATCH_B1
|
||||||
|
rts
|
||||||
|
|
||||||
|
+ lda c64.TIME_LO
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
- lda c64.TIME_LO
|
||||||
|
cmp P8ZP_SCRATCH_B1
|
||||||
|
beq -
|
||||||
|
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
bne +
|
||||||
|
dec P8ZP_SCRATCH_W1+1
|
||||||
|
+ dec P8ZP_SCRATCH_W1
|
||||||
|
jmp _loop
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub waitvsync() clobbers(A) {
|
asmsub waitvsync() clobbers(A) {
|
||||||
@ -519,6 +576,17 @@ sys {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||||
|
; Called when the compiler wants to assign a string value to another string.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jmp prog8_lib.strcpy
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
; note: only works for NON-OVERLAPPING memory regions!
|
; note: only works for NON-OVERLAPPING memory regions!
|
||||||
; note: can't be inlined because is called from asm as well
|
; note: can't be inlined because is called from asm as well
|
||||||
@ -635,7 +703,10 @@ _longcopy
|
|||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
%asm {{
|
%asm {{
|
||||||
|
lda #31
|
||||||
|
sta $01 ; bank the kernal in
|
||||||
jsr c64.CLRCHN ; reset i/o channels
|
jsr c64.CLRCHN ; reset i/o channels
|
||||||
|
jsr c64.enable_runstop_and_charsetswitch
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
txs
|
txs
|
||||||
rts ; return to original caller
|
rts ; return to original caller
|
||||||
@ -658,6 +729,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
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user