mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
585 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
520a142992 | |||
6ff56dc0bb | |||
1e63615592 | |||
3e62ffed0a | |||
b133d51a83 | |||
037b89f018 | |||
20d06d9f9d | |||
156cf7315c | |||
e2886e5303 | |||
c6cf330e70 | |||
6be3b62d78 | |||
c57af5e81b | |||
f7431f809e | |||
ea43c34de8 | |||
fb6e9fa58f | |||
b2ce1e8029 | |||
d90c51220f | |||
d1b14b68fa | |||
d911728611 | |||
86a7200012 | |||
6ddb7453e1 | |||
ad2355f8d3 | |||
582c498fe3 | |||
0a0c58d450 | |||
0dc592b819 | |||
f46300016d | |||
3e1a7c6102 | |||
f07065bf84 | |||
6d79903eb3 | |||
e166329f34 | |||
bb1bf6a88c | |||
30cbb6c9a8 | |||
4e33ab1e89 | |||
5494f309c0 | |||
3b6e7eccdd | |||
e41d6787bb | |||
ed30108961 | |||
12712ef812 | |||
0307f6b42c | |||
3e44620966 | |||
7424f1f768 | |||
b5331d821c | |||
27f6d47efa | |||
dbc7ad2ec4 | |||
7b27d270a2 | |||
97b3a0b093 | |||
06b38506d1 | |||
fd581ffc37 | |||
ff57c5e9d3 | |||
9b16d7c786 | |||
4c1bb18956 | |||
7d2bf892b1 | |||
a99e77093f | |||
92737bb695 | |||
9b81955544 | |||
4a0031080a | |||
40e9fba312 | |||
e227cc92ff | |||
73dbdbcbe6 | |||
3961f26635 | |||
e51c274a18 | |||
e75d0c58a9 | |||
9a798360f4 | |||
844ad09464 | |||
1e1d1efd90 | |||
240e6835c2 | |||
61398ee8f8 | |||
e6e84859b7 | |||
abcdd331db | |||
775d136b91 | |||
dc93691fd9 | |||
48d782c69c | |||
0a04e626d7 | |||
e7c4bf5ebf | |||
546a416f7e | |||
179a7a2792 | |||
251b6fcf70 | |||
ab1fffb721 | |||
da352a322c | |||
7d20458e82 | |||
5a54066f81 | |||
a58e5a3399 | |||
9872f43cbf | |||
1078cc4642 | |||
db7ae028b2 | |||
2b6f5dbd59 | |||
f7aa0c45df | |||
a72d58cdf9 | |||
067283834a | |||
cf362c4a61 | |||
496245c801 | |||
859ab36347 | |||
1d740c7c36 | |||
a03c4c3659 | |||
094ecceaac | |||
2812736ae5 | |||
6f87f8706c | |||
38beebe720 | |||
fc1c3c6808 | |||
96ba895b84 | |||
df35dfe3bf | |||
c5504c6657 | |||
530e109433 | |||
6cce47b2f1 | |||
6185d5eca1 | |||
685ad1746e | |||
891f870ec0 | |||
ad9933f0f6 | |||
1b86117754 | |||
eeb3c968d6 | |||
406658a10f | |||
6a0551cea1 | |||
553f3b45d2 | |||
064a8e785c | |||
21e9723bb2 | |||
60b2c44a44 | |||
c4fe3ecc0a | |||
2f18a8f6d0 | |||
5ac784e18a | |||
7a2164b4d0 | |||
0a43eae184 | |||
3117e2b2a3 | |||
41fece4643 | |||
7aa807ec7f | |||
4d16e1e14a | |||
73fc18099e | |||
e34dac8dbb | |||
af0e7f7187 | |||
a3a6812608 | |||
2725c4ad4d | |||
c8cd6e9460 | |||
0cd27d6129 | |||
b47fc1c020 | |||
de6ef7ef5e | |||
f95fe8f1da | |||
bd0dee5db5 | |||
c13b7fd883 | |||
f7e74b3088 | |||
343f01d5e1 | |||
08bacdd090 | |||
41b1c80492 | |||
e5d7316e5d | |||
b043c3a6da | |||
98b2855b9c | |||
f3c52c409f | |||
1307bdc612 | |||
8c2e6971fc | |||
1903990f30 | |||
7d67005709 | |||
9acc2f92d1 | |||
72dfb0bda3 | |||
1635612430 | |||
abda837d2f | |||
101fb0b8aa | |||
10de7dc1f9 | |||
d2309b8114 | |||
6bdd81623f | |||
f538c9f0c3 | |||
8ae3bad6f7 | |||
77de99b383 | |||
312949f336 | |||
1ab635bd7e | |||
b35abd548c | |||
30e1c3307c | |||
08e052380a | |||
0e824c35cc | |||
548374ac2d | |||
9ad79fefc9 | |||
49d37c016e | |||
7c70c79a84 | |||
6916b8bff7 | |||
73dfb5f443 | |||
69b9dfa468 | |||
ab61b8ba0a | |||
5c8c64242f | |||
ddf96943f0 | |||
e773be2f58 | |||
f965804e6d | |||
ec078eba72 | |||
1815cb1bc3 | |||
7b3cd71085 | |||
06128b5d07 | |||
990c8e1f18 | |||
a170506356 | |||
5ecf2a3357 | |||
fa48746ba9 | |||
e2b8c069d7 | |||
14407bd1aa | |||
08db72903c | |||
46f9fab140 | |||
b7d06f2c0a | |||
118196a0bf | |||
586ce1fc80 | |||
adb979df38 | |||
3401cb5b4a | |||
ebf1f12e97 | |||
5766208207 | |||
4bf4771f08 | |||
0e87db9eb7 | |||
1e053783f3 | |||
7afc96112b | |||
7bb41a30ed | |||
3d1b0eb843 | |||
5b9af0b5ae | |||
9219ec539d | |||
c8bd57cd4d | |||
53bf8c09fd | |||
651c383668 | |||
9ed7587e3e | |||
674295e800 | |||
6b02f2eea0 | |||
5237e55326 | |||
3b59592110 | |||
72640ae058 | |||
d916027e75 | |||
8966d2aa06 | |||
de7ea04f54 | |||
bf71fabe0e | |||
b3368acb33 | |||
87220c6697 | |||
a3b5c2ad71 | |||
fb4c1473c5 | |||
2bb2502d20 | |||
fe51698579 | |||
0f0f40bff3 | |||
a798fe72d3 | |||
fba98d03a5 | |||
7dd2517f67 | |||
641477d6f6 | |||
8e56656c8d | |||
564a6a1f62 | |||
69f0c80cd7 | |||
6fcb51cea2 | |||
c58b8a4973 | |||
c8f4ab4f06 | |||
e425c4cca8 | |||
056ec986c2 | |||
de3b2fb95b | |||
789e39c719 | |||
b29c3152db | |||
3831679772 | |||
596f9566d8 | |||
124befe9d6 | |||
895534f32b | |||
50c16fe6de | |||
b092d1a5d3 | |||
a9b45630d7 | |||
c1a39c269e | |||
6fa3f0b6cd | |||
9e5e3d1559 | |||
7135205299 | |||
d99d977d2b | |||
7dd7e562bc | |||
75d857027e | |||
17694c1d01 | |||
749ad700d8 | |||
8f3df3039a | |||
02c315c194 | |||
b697375573 | |||
c57ef7725e | |||
3ae07503f2 | |||
9a0341adde | |||
96225efd96 | |||
c3bd904f41 | |||
74257163b1 | |||
7bc75fd220 | |||
a23281afab | |||
9e90dbdde6 | |||
1e8d8e40a2 | |||
583e208c1e | |||
9b91c427a1 | |||
196c5e9c24 | |||
d8f7feb672 | |||
7c889f17b9 | |||
c15a75556d | |||
5275c2e35f | |||
5267e06969 | |||
b62183adcb | |||
5d2dec1803 | |||
4a98dab948 | |||
9f8c70b326 | |||
a65404e63a | |||
05a1ddad05 | |||
4be3d63c0e | |||
de6ce4a46e | |||
7a9e5afb93 | |||
b2876b0a03 | |||
b66f66fe6a | |||
30f04962d4 | |||
0feeb88024 | |||
b799f2e05b | |||
56d21de001 | |||
7b54aa0c7d | |||
6e11b8ada1 | |||
98d25fc4e9 | |||
79405f47f6 | |||
1c7c4fc3b0 | |||
97e84d0977 | |||
9906b58818 | |||
371f084884 | |||
1c10839c14 | |||
c55fdd9834 | |||
67b0890a6e | |||
4da4f96669 | |||
60a64c2558 | |||
d4153da8b9 | |||
fc33ab8905 | |||
a123c64f59 | |||
a090fe3834 | |||
8fa84de28e | |||
3e3da38de1 | |||
a3be8ccc87 | |||
cdfef30c22 | |||
cabf1e82e8 | |||
608dc5e284 | |||
836d40072f | |||
431401d90e | |||
6da83e2bd7 | |||
7bccfc0006 | |||
e051e09c1d | |||
1462c57d0c | |||
77c2b2b326 | |||
3cf9b9d9a5 | |||
629117e594 | |||
08f87c321f | |||
1ff13723fe | |||
510bda1b28 | |||
890327b381 | |||
b21f7411dd | |||
5df623bd2e | |||
1e9d249f71 | |||
a7b5949e6a | |||
02010170ce | |||
35998142fe | |||
75ea453bf4 | |||
33061aaa0d | |||
e342311bef | |||
3d743a1ba1 | |||
abca618008 | |||
0d2c3901a3 | |||
d901a1531f | |||
d8d56b195f | |||
a52699717c | |||
98315de723 | |||
68d2f7d4c0 | |||
c812b5ee09 | |||
dcf487bdc1 | |||
547b1d3720 | |||
84f75f4156 | |||
ff69da3fa2 | |||
edffe92a24 | |||
b6fe40ada4 | |||
837804b231 | |||
81deed143b | |||
900cdd3fa1 | |||
0018dc6ce7 | |||
c92f914081 | |||
0498444ef2 | |||
ce3c34e458 | |||
20401b99d8 | |||
397f98513b | |||
e545ea9504 | |||
b867d8f731 | |||
9a68864b67 | |||
72d7178762 | |||
fbcd9a0c1d | |||
c3144a20db | |||
5b56e0462d | |||
b7fffbb6df | |||
1f346230e3 | |||
a2860a7c8c | |||
df997e5d3b | |||
a67a82c921 | |||
ea0fe8d3d2 | |||
2560042ac7 | |||
3d1d0696b9 | |||
83f893f50b | |||
9ecf95b075 | |||
7748c261da | |||
a2db44f80c | |||
b438d8aec0 | |||
4ac169b210 | |||
56dc6d7f1e | |||
45b8762188 | |||
cafab98d10 | |||
9256f910f0 | |||
32068a832a | |||
47c2c0376a | |||
f0dadc4a43 | |||
960b60cd2d | |||
d6abd72e55 | |||
0a568f2530 | |||
c52aa648c0 | |||
3d23b39f4c | |||
f3a4048ebf | |||
1b07637cc4 | |||
68b75fd558 | |||
7c5ec1853d | |||
e8f4686430 | |||
02348924d0 | |||
69dcb4dbda | |||
c838821615 | |||
8b4ac7801f | |||
64a411628d | |||
e8e25c6fd6 | |||
62485b6851 | |||
54025d2bf5 | |||
f5ebf79e71 | |||
66d5490702 | |||
42fe052f9f | |||
58d9c46a9b | |||
e4648e2138 | |||
110e047681 | |||
17d403d812 | |||
0a53bd4956 | |||
e52d05c7db | |||
b00db4f8a2 | |||
0c2f30fd45 | |||
e08871c637 | |||
ff715881bc | |||
0e2e5ffa52 | |||
8095c4c155 | |||
e86246a985 | |||
625aaa02eb | |||
787e35c9f3 | |||
8887e6af91 | |||
dde4c751da | |||
3c39baf1d6 | |||
b292124f3c | |||
c0035ba1a2 | |||
2491509c6a | |||
107935ed31 | |||
31491c62c5 | |||
eacf8b896a | |||
7936fc5bd8 | |||
adfaddbcf4 | |||
74db5c6be7 | |||
f9399bcce7 | |||
87600b23db | |||
cedfb17b18 | |||
fa4c83df6b | |||
42c8720e8b | |||
b334d89715 | |||
4f5d36a84d | |||
8f379e2262 | |||
fa11a6e18b | |||
52bedce8f4 | |||
4c82af36e6 | |||
dafa0d9138 | |||
2e0450d7ed | |||
6af3209d4d | |||
5d362047e2 | |||
f48d6ca9f8 | |||
964e8e0a17 | |||
1f60a2d8b9 | |||
5fd83f2757 | |||
c80df4140b | |||
53e1729e2f | |||
ab2d1122a9 | |||
5190594c8a | |||
c858ceeb58 | |||
f0f52b9166 | |||
00c6f74481 | |||
2177ba0ed2 | |||
3483515346 | |||
75a06d2a40 | |||
53ac11983b | |||
69f4a4d4f8 | |||
222bcb808f | |||
686483f51a | |||
8df3da11e3 | |||
84dafda0e4 | |||
b909facfe5 | |||
7780d94de1 | |||
f2c440e466 | |||
4937e004b5 | |||
4cb383dccb | |||
c8a4b6f23c | |||
857724c7e6 | |||
a9b0400d13 | |||
2d1e5bbc7e | |||
60627ce756 | |||
7961a09d16 | |||
613efcacc7 | |||
7e8db16e18 | |||
1fbbed7e23 | |||
984272beb4 | |||
b9ce94bb68 | |||
f4c4ee78d9 |
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
4
.idea/compiler.xml
generated
4
.idea/compiler.xml
generated
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="1200" />
|
<option name="BUILD_PROCESS_HEAP_SIZE" value="3000" />
|
||||||
<bytecodeTargetLevel target="11" />
|
<bytecodeTargetLevel target="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
@ -7,6 +7,7 @@
|
|||||||
<language isEnabled="false" name="Groovy" />
|
<language isEnabled="false" name="Groovy" />
|
||||||
</Languages>
|
</Languages>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||||
<option name="processCode" value="false" />
|
<option name="processCode" value="false" />
|
||||||
|
10
.idea/libraries/antlr_antlr4.xml
generated
10
.idea/libraries/antlr_antlr4.xml
generated
@ -1,15 +1,15 @@
|
|||||||
<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.2">
|
<properties maven-id="org.antlr:antlr4:4.10.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.2/antlr4-4.9.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.10.1/antlr4-4.10.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.9.2/antlr4-runtime-4.9.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.10.1/antlr4-runtime-4.10.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/ST4-4.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.3/ST4-4.3.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/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.0.4/javax.json-1.0.4.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
|
10
.idea/libraries/hamcrest.xml
generated
10
.idea/libraries/hamcrest.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="hamcrest" type="repository">
|
|
||||||
<properties maven-id="org.hamcrest:hamcrest:2.2" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
23
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
23
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.1.0" />
|
||||||
|
<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$/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$/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$/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/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/kotest/kotest-common-jvm/5.1.0/kotest-common-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!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
24
.idea/libraries/io_kotest_property_jvm.xml
generated
Normal file
24
.idea/libraries/io_kotest_property_jvm.xml
generated
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.property.jvm" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-property-jvm:5.1.0" />
|
||||||
|
<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-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!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
53
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
53
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.1.0" />
|
||||||
|
<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-framework-api-jvm/5.1.0/kotest-framework-api-jvm-5.1.0.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.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/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.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/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.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$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.0/kotlinx-coroutines-debug-1.6.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$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.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.1.0/kotest-assertions-core-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$/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.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$/io/mockk/mockk/1.12.2/mockk-1.12.2.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-dsl/1.12.2/mockk-dsl-1.12.2.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-agent-jvm/1.12.2/mockk-agent-jvm-1.12.2.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-common/1.12.2/mockk-agent-common-1.12.2.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-agent/1.12.5/byte-buddy-agent-1.12.5.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$/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$/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/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-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/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!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
@ -1,8 +1,8 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3" />
|
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.4" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.3/kotlinx-cli-jvm-0.3.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.4/kotlinx-cli-jvm-0.3.4.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
17
.idea/libraries/junit_jupiter.xml
generated
17
.idea/libraries/junit_jupiter.xml
generated
@ -1,17 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="junit.jupiter" type="repository">
|
|
||||||
<properties maven-id="org.junit.jupiter:junit-jupiter:5.7.2" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.7.2/junit-jupiter-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/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.7.2/junit-jupiter-params-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.7.2/junit-jupiter-engine-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
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.12" />
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.12/kotlin-result-jvm-1.1.12.jar!/" />
|
<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$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.10/kotlin-stdlib-jdk8-1.5.10.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.5.10/kotlin-stdlib-1.5.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/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.10/kotlin-stdlib-jdk7-1.5.10.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-common/1.5.10/kotlin-stdlib-common-1.5.10.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 />
|
||||||
|
6
.idea/libraries/slf4j_simple.xml
generated
6
.idea/libraries/slf4j_simple.xml
generated
@ -1,9 +1,9 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="slf4j.simple" type="repository">
|
<library name="slf4j.simple" type="repository">
|
||||||
<properties maven-id="org.slf4j:slf4j-simple:1.7.30" />
|
<properties maven-id="org.slf4j:slf4j-simple:1.7.36" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.30/slf4j-simple-1.7.30.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@ -16,7 +16,10 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
<type id="Python" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -2,16 +2,20 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
|
<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$/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$/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" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" filepath="$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||||
<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$/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" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -3,4 +3,4 @@
|
|||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -12,6 +12,9 @@
|
|||||||
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
|
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
|
||||||
|
|
||||||
#### Steps to take, in conceptual (!) order:
|
#### Steps to take, in conceptual (!) order:
|
||||||
|
|
||||||
|
(note: all these steps have been implemented, rejected or otherwise solved now.)
|
||||||
|
|
||||||
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
|
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
|
||||||
- from the local file system (use case: user programs)
|
- from the local file system (use case: user programs)
|
||||||
- from resources (prog8lib)
|
- from resources (prog8lib)
|
||||||
|
5
LICENSE
5
LICENSE
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
This sofware license is for Prog8 the compiler + associated libraries.
|
This sofware license is for Prog8 the compiler + associated libraries.
|
||||||
The software generated by running the compiler is excluded from this.
|
|
||||||
|
Any and all outputs generated by the compiler (intermediary
|
||||||
|
source code and compiled binary programs) are excluded from that
|
||||||
|
and you can do with them whatever you want.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -37,7 +37,8 @@ What does Prog8 provide?
|
|||||||
- small program boilerplate/compilersupport overhead
|
- small program boilerplate/compilersupport overhead
|
||||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||||
|
- ``in`` expression for concise and efficient multi-value/containment check
|
||||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||||
@ -52,11 +53,12 @@ What does Prog8 provide?
|
|||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
|
|
||||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||||
|
|
||||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
- "c64": Commodore-64 (6502 like CPU)
|
||||||
|
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
42
codeAst/build.gradle
Normal file
42
codeAst/build.gradle
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':codeCore')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
14
codeAst/codeAst.iml
Normal file
14
codeAst/codeAst.iml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?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" />
|
||||||
|
<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" />
|
||||||
|
</component>
|
||||||
|
</module>
|
217
codeAst/src/prog8/code/SymbolTable.kt
Normal file
217
codeAst/src/prog8/code/SymbolTable.kt
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree structure containing all symbol definitions in the program
|
||||||
|
* (blocks, subroutines, variables (all types) and labels).
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
* 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>()
|
||||||
|
fun flatten(node: StNode) {
|
||||||
|
result[node.scopedName] = node
|
||||||
|
node.children.values.forEach { flatten(it) }
|
||||||
|
}
|
||||||
|
children.values.forEach { flatten(it) }
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
val allVariables: Collection<StStaticVariable> by lazy {
|
||||||
|
val vars = mutableListOf<StStaticVariable>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.STATICVAR)
|
||||||
|
vars.add(child.value as StStaticVariable)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lookup(scopedName: List<String>) = flat[scopedName]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class StNodeType {
|
||||||
|
GLOBAL,
|
||||||
|
// MODULE, // not used with current scoping rules
|
||||||
|
BLOCK,
|
||||||
|
SUBROUTINE,
|
||||||
|
ROMSUB,
|
||||||
|
LABEL,
|
||||||
|
STATICVAR,
|
||||||
|
MEMVAR,
|
||||||
|
CONSTANT,
|
||||||
|
BUILTINFUNC
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open class StNode(val name: String,
|
||||||
|
val type: StNodeType,
|
||||||
|
val position: Position,
|
||||||
|
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||||
|
) {
|
||||||
|
|
||||||
|
lateinit var parent: StNode
|
||||||
|
|
||||||
|
val scopedName: List<String> by lazy {
|
||||||
|
if(type== StNodeType.GLOBAL)
|
||||||
|
emptyList()
|
||||||
|
else
|
||||||
|
parent.scopedName + name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lookup(name: String) =
|
||||||
|
lookupUnqualified(name)
|
||||||
|
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? {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookupUnqualified(name: String): StNode? {
|
||||||
|
// first consider the builtin functions
|
||||||
|
var globalscope = this
|
||||||
|
while(globalscope.type!= StNodeType.GLOBAL)
|
||||||
|
globalscope = globalscope.parent
|
||||||
|
val globalNode = globalscope.children[name]
|
||||||
|
if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC)
|
||||||
|
return globalNode
|
||||||
|
|
||||||
|
// search for the unqualified name in the current scope or its parent scopes
|
||||||
|
var scope=this
|
||||||
|
while(true) {
|
||||||
|
val node = scope.children[name]
|
||||||
|
if(node!=null)
|
||||||
|
return node
|
||||||
|
if(scope.type== StNodeType.GLOBAL)
|
||||||
|
return null
|
||||||
|
else
|
||||||
|
scope = scope.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
children[child.name] = child
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StStaticVariable(name: String,
|
||||||
|
val dt: DataType,
|
||||||
|
val initialNumericValue: Double?,
|
||||||
|
val initialStringValue: StString?,
|
||||||
|
val initialArrayValue: StArray?,
|
||||||
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
|
val zpwish: ZeropageWish,
|
||||||
|
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(length!=null) {
|
||||||
|
require(initialNumericValue == null)
|
||||||
|
if(initialArrayValue!=null)
|
||||||
|
require(length == initialArrayValue.size)
|
||||||
|
}
|
||||||
|
if(initialNumericValue!=null)
|
||||||
|
require(dt in NumericDatatypes)
|
||||||
|
if(initialArrayValue!=null)
|
||||||
|
require(dt in ArrayDatatypes)
|
||||||
|
if(initialStringValue!=null) {
|
||||||
|
require(dt == DataType.STR)
|
||||||
|
require(length == initialStringValue.first.length+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name dt=$dt zpw=$zpwish")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StConstant(name: String, val dt: DataType, val value: Double, position: 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) :
|
||||||
|
StNode(name, StNodeType.MEMVAR, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name dt=$dt address=${address.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StSub(name: String, val parameters: List<StSubroutineParameter>, position: Position) :
|
||||||
|
StNode(name, StNodeType.SUBROUTINE, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StRomSub(name: String, val address: UInt, parameters: List<StSubroutineParameter>, position: Position) :
|
||||||
|
StNode(name, StNodeType.ROMSUB, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name address=${address.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StSubroutineParameter(val name: String, val type: DataType)
|
||||||
|
class StArrayElement(val number: Double?, val addressOf: List<String>?)
|
||||||
|
|
||||||
|
typealias StString = Pair<String, Encoding>
|
||||||
|
typealias StArray = List<StArrayElement>
|
144
codeAst/src/prog8/code/ast/AstBase.kt
Normal file
144
codeAst/src/prog8/code/ast/AstBase.kt
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
// New (work-in-progress) simplified AST for the code generator.
|
||||||
|
|
||||||
|
|
||||||
|
sealed class PtNode(val position: Position) {
|
||||||
|
|
||||||
|
val children = mutableListOf<PtNode>()
|
||||||
|
lateinit var parent: PtNode
|
||||||
|
|
||||||
|
fun printIndented(indent: Int) {
|
||||||
|
print(" ".repeat(indent))
|
||||||
|
print("${this.javaClass.simpleName} ")
|
||||||
|
printProperties()
|
||||||
|
println()
|
||||||
|
children.forEach { it.printIndented(indent+1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun printProperties()
|
||||||
|
|
||||||
|
fun add(child: PtNode) {
|
||||||
|
children.add(child)
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(index: Int, child: PtNode) {
|
||||||
|
children.add(index, child)
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||||
|
fun definingSub() = findParentNode<PtSub>(this)
|
||||||
|
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtNodeGroup : PtNode(Position.DUMMY) {
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
|
||||||
|
val scopedName: List<String> by lazy {
|
||||||
|
var namedParent: PtNode = this.parent
|
||||||
|
if(namedParent is PtProgram)
|
||||||
|
listOf(name)
|
||||||
|
else {
|
||||||
|
while (namedParent !is PtNamedNode)
|
||||||
|
namedParent = namedParent.parent
|
||||||
|
namedParent.scopedName + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtProgram(
|
||||||
|
val name: String,
|
||||||
|
val memsizer: IMemSizer,
|
||||||
|
val encoding: IStringEncoding
|
||||||
|
) : PtNode(Position.DUMMY) {
|
||||||
|
fun print() = printIndented(0)
|
||||||
|
override fun printProperties() {
|
||||||
|
print("'$name'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||||
|
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||||
|
|
||||||
|
fun allBlocks(): Sequence<PtBlock> =
|
||||||
|
children.asSequence().filterIsInstance<PtBlock>()
|
||||||
|
|
||||||
|
fun entrypoint(): PtSub? =
|
||||||
|
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBlock(name: String,
|
||||||
|
val address: UInt?,
|
||||||
|
val library: Boolean,
|
||||||
|
val forceOutput: Boolean,
|
||||||
|
val alignment: BlockAlignment,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
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) {
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBreakpoint(position: Position): PtNode(position) {
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("filename=$file offset=$offset length=$length")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtNop(position: Position): PtNode(position) {
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtScopeVarsDecls(position: Position): PtNode(position) {
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// find the parent node of a specific type or interface
|
||||||
|
// (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
|
||||||
|
}
|
216
codeAst/src/prog8/code/ast/AstExpressions.kt
Normal file
216
codeAst/src/prog8/code/ast/AstExpressions.kt
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
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) {
|
||||||
|
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.targetName==targetName
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(type!=DataType.FLOAT) {
|
||||||
|
val rounded = round(number)
|
||||||
|
if (rounded != number)
|
||||||
|
throw IllegalArgumentException("refused rounding of float to avoid loss of precision")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
|
||||||
|
init {
|
||||||
|
if(!void)
|
||||||
|
require(type!=DataType.UNDEFINED)
|
||||||
|
}
|
||||||
|
|
||||||
|
val segments: List<PtExpression>
|
||||||
|
get() = children.map { it as PtExpression }
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
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\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
219
codeAst/src/prog8/code/ast/AstStatements.kt
Normal file
219
codeAst/src/prog8/code/ast/AstStatements.kt
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
class PtAsmSub(
|
||||||
|
name: String,
|
||||||
|
val address: UInt?,
|
||||||
|
val clobbers: Set<CpuRegister>,
|
||||||
|
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
|
||||||
|
val retvalRegisters: List<RegisterOrStatusflag>,
|
||||||
|
val inline: Boolean,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$name inline=$inline")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtSub(
|
||||||
|
name: String,
|
||||||
|
val parameters: List<PtSubroutineParameter>,
|
||||||
|
val returntype: DataType?,
|
||||||
|
val inline: Boolean,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtSubroutineParameter(val name: String, val type: DataType, position: Position): PtNode(position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$type $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtAssignment(position: Position) : PtNode(position) {
|
||||||
|
val target: PtAssignTarget
|
||||||
|
get() = children[0] as PtAssignTarget
|
||||||
|
val value: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
|
override fun printProperties() { }
|
||||||
|
|
||||||
|
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.targetName==source.targetName
|
||||||
|
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)?.targetName==target.targetName)
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
val identifier: PtIdentifier?
|
||||||
|
get() = children.single() as? PtIdentifier
|
||||||
|
val array: PtArrayIndexer?
|
||||||
|
get() = children.single() as? PtArrayIndexer
|
||||||
|
val memory: PtMemoryByte?
|
||||||
|
get() = children.single() as? PtMemoryByte
|
||||||
|
|
||||||
|
val type: DataType
|
||||||
|
get() {
|
||||||
|
return when(val tgt = children.single()) {
|
||||||
|
is PtIdentifier -> tgt.type
|
||||||
|
is PtArrayIndexer -> tgt.type
|
||||||
|
is PtMemoryByte -> tgt.type
|
||||||
|
else -> throw AssemblyError("weird target $tgt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
|
||||||
|
val trueScope: PtNodeGroup
|
||||||
|
get() = children[0] as PtNodeGroup
|
||||||
|
val falseScope: PtNodeGroup
|
||||||
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print(condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtForLoop(position: Position) : PtNode(position) {
|
||||||
|
val variable: PtIdentifier
|
||||||
|
get() = children[0] as PtIdentifier
|
||||||
|
val iterable: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
val statements: PtNodeGroup
|
||||||
|
get() = children[2] as PtNodeGroup
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtIfElse(position: Position) : PtNode(position) {
|
||||||
|
val condition: PtBinaryExpression
|
||||||
|
get() = children[0] as PtBinaryExpression
|
||||||
|
val ifScope: PtNodeGroup
|
||||||
|
get() = children[1] as PtNodeGroup
|
||||||
|
val elseScope: PtNodeGroup
|
||||||
|
get() = children[2] as PtNodeGroup
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtJump(val identifier: PtIdentifier?,
|
||||||
|
val address: UInt?,
|
||||||
|
val generatedLabel: String?,
|
||||||
|
position: Position) : PtNode(position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
identifier?.printProperties()
|
||||||
|
if(address!=null) print(address.toHex())
|
||||||
|
if(generatedLabel!=null) print(generatedLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
||||||
|
val target: PtAssignTarget
|
||||||
|
get() = children.single() as PtAssignTarget
|
||||||
|
|
||||||
|
override fun printProperties() {
|
||||||
|
print(operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||||
|
val count: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val statements: PtNodeGroup
|
||||||
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtReturn(position: Position) : PtNode(position) {
|
||||||
|
val hasValue = children.any()
|
||||||
|
val value: PtExpression?
|
||||||
|
get() {
|
||||||
|
return if(children.any())
|
||||||
|
children.single() as PtExpression
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$type $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("$type $name = $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtMemMapped(name: String, val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position) {
|
||||||
|
override fun printProperties() {
|
||||||
|
print("&$type $name = ${address.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtWhen(position: Position) : PtNode(position) {
|
||||||
|
val value: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val choices: PtNodeGroup
|
||||||
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
||||||
|
val values: PtNodeGroup
|
||||||
|
get() = children[0] as PtNodeGroup
|
||||||
|
val statements: PtNodeGroup
|
||||||
|
get() = children[1] as PtNodeGroup
|
||||||
|
override fun printProperties() {}
|
||||||
|
}
|
43
codeCore/build.gradle
Normal file
43
codeCore/build.gradle
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// should have no dependencies to other modules
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
14
codeCore/codeCore.iml
Normal file
14
codeCore/codeCore.iml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?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" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
24
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
24
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
|
||||||
|
class CompilationOptions(val output: OutputType,
|
||||||
|
val launcher: CbmPrgLauncherType,
|
||||||
|
val zeropage: ZeropageType,
|
||||||
|
val zpReserved: List<UIntRange>,
|
||||||
|
val floats: Boolean,
|
||||||
|
val noSysInit: Boolean,
|
||||||
|
val compTarget: ICompilationTarget,
|
||||||
|
// these are set later, based on command line arguments or options in the source code:
|
||||||
|
var loadAddress: UInt,
|
||||||
|
var slowCodegenWarnings: Boolean = false,
|
||||||
|
var optimize: Boolean = false,
|
||||||
|
var optimizeFloatExpressions: Boolean = false,
|
||||||
|
var dontReinitGlobals: Boolean = false,
|
||||||
|
var asmQuiet: Boolean = false,
|
||||||
|
var asmListfile: Boolean = false,
|
||||||
|
var experimentalCodegen: Boolean = false,
|
||||||
|
var outputDir: Path = Path("")
|
||||||
|
)
|
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("")
|
||||||
|
}
|
@ -1,9 +1,4 @@
|
|||||||
package prog8.ast.base
|
package prog8.code.core
|
||||||
|
|
||||||
import prog8.ast.Node
|
|
||||||
|
|
||||||
|
|
||||||
/**************************** AST Data classes ****************************/
|
|
||||||
|
|
||||||
enum class DataType {
|
enum class DataType {
|
||||||
UBYTE, // pass by value
|
UBYTE, // pass by value
|
||||||
@ -23,39 +18,36 @@ enum class DataType {
|
|||||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||||
*/
|
*/
|
||||||
infix fun isAssignableTo(targetType: DataType) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
|
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
|
||||||
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 == FLOAT
|
||||||
STR -> targetType == STR
|
STR -> targetType.oneOf(STR, UWORD)
|
||||||
in ArrayDatatypes -> targetType == this
|
in ArrayDatatypes -> targetType == this
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun oneOf(vararg types: DataType) = this in types
|
fun oneOf(vararg types: DataType) = this in types
|
||||||
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
|
||||||
infix fun isNotAssignableTo(targetType: DataType) = !this.isAssignableTo(targetType)
|
|
||||||
infix fun isNotAssignableTo(targetTypes: Set<DataType>) = !this.isAssignableTo(targetTypes)
|
|
||||||
|
|
||||||
infix fun largerThan(other: DataType) =
|
infix fun largerThan(other: DataType) =
|
||||||
when {
|
when {
|
||||||
this == other -> false
|
this == other -> false
|
||||||
this in ByteDatatypes -> false
|
this in ByteDatatypes -> false
|
||||||
this in WordDatatypes -> other in ByteDatatypes
|
this in WordDatatypes -> other in ByteDatatypes
|
||||||
this== STR && other== UWORD || this== UWORD && other== STR -> false
|
this== STR && other== UWORD || this== UWORD && other== STR -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun equalsSize(other: DataType) =
|
infix fun equalsSize(other: DataType) =
|
||||||
when {
|
when {
|
||||||
this == other -> true
|
this == other -> true
|
||||||
this in ByteDatatypes -> other in ByteDatatypes
|
this in ByteDatatypes -> other in ByteDatatypes
|
||||||
this in WordDatatypes -> other in WordDatatypes
|
this in WordDatatypes -> other in WordDatatypes
|
||||||
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
@ -110,28 +102,25 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class VarDeclType {
|
|
||||||
VAR,
|
|
||||||
CONST,
|
|
||||||
MEMORY
|
|
||||||
}
|
|
||||||
|
|
||||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
|
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
|
||||||
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)
|
||||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||||
|
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||||
|
val IntegerArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W)
|
||||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||||
val IterableDatatypes = arrayOf(
|
val IterableDatatypes = arrayOf(
|
||||||
@ -143,19 +132,19 @@ val IterableDatatypes = arrayOf(
|
|||||||
val PassByValueDatatypes = NumericDatatypes
|
val PassByValueDatatypes = NumericDatatypes
|
||||||
val PassByReferenceDatatypes = IterableDatatypes
|
val PassByReferenceDatatypes = IterableDatatypes
|
||||||
val ArrayToElementTypes = mapOf(
|
val ArrayToElementTypes = mapOf(
|
||||||
DataType.STR to DataType.UBYTE,
|
DataType.STR to DataType.UBYTE,
|
||||||
DataType.ARRAY_B to DataType.BYTE,
|
DataType.ARRAY_B to DataType.BYTE,
|
||||||
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
|
||||||
)
|
)
|
||||||
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
|
||||||
)
|
)
|
||||||
val Cx16VirtualRegisters = arrayOf(
|
val Cx16VirtualRegisters = arrayOf(
|
||||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||||
@ -165,32 +154,29 @@ val Cx16VirtualRegisters = arrayOf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// find the parent node of a specific type or interface
|
|
||||||
// (useful to figure out in what namespace/block something is defined, etc.)
|
enum class OutputType {
|
||||||
inline fun <reified T> findParentNode(node: Node): T? {
|
RAW,
|
||||||
var candidate = node.parent
|
PRG,
|
||||||
while(candidate !is T && candidate !is ParentSentinel)
|
XEX
|
||||||
candidate = candidate.parent
|
|
||||||
return if(candidate is ParentSentinel)
|
|
||||||
null
|
|
||||||
else
|
|
||||||
candidate as T
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ParentSentinel : Node {
|
enum class CbmPrgLauncherType {
|
||||||
override val position = Position("<<sentinel>>", 0, 0, 0)
|
BASIC,
|
||||||
override var parent: Node = this
|
NONE
|
||||||
override fun linkParents(parent: Node) {}
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
replacement.parent = this
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
enum class ZeropageType {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
BASICSAFE,
|
||||||
fun toClickableStr(): String = "$file:$line:$startCol:"
|
FLOATSAFE,
|
||||||
|
KERNALSAFE,
|
||||||
companion object {
|
FULL,
|
||||||
val DUMMY = Position("<dummy>", 0, 0, 0)
|
DONTUSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ZeropageWish {
|
||||||
|
REQUIRE_ZEROPAGE,
|
||||||
|
PREFER_ZEROPAGE,
|
||||||
|
DONTCARE,
|
||||||
|
NOT_IN_ZEROPAGE
|
||||||
}
|
}
|
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
class InternalCompilerException(message: String?) : Exception(message)
|
||||||
|
|
||||||
|
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||||
|
|
||||||
|
class ErrorsReportedException(message: String?) : Exception(message)
|
12
codeCore/src/prog8/code/core/IAssemblyGenerator.kt
Normal file
12
codeCore/src/prog8/code/core/IAssemblyGenerator.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
interface IAssemblyGenerator {
|
||||||
|
fun compileToAssembly(): IAssemblyProgram?
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAssemblyProgram {
|
||||||
|
val name: String
|
||||||
|
fun assemble(options: CompilationOptions): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
11
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
11
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||||
|
val name: String
|
||||||
|
val machine: IMachineDefinition
|
||||||
|
val supportedEncodings: Set<Encoding>
|
||||||
|
val defaultEncoding: Encoding
|
||||||
|
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
|
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String
|
||||||
|
}
|
@ -1,7 +1,4 @@
|
|||||||
package prog8.compilerinterface
|
package prog8.code.core
|
||||||
|
|
||||||
import prog8.ast.base.Position
|
|
||||||
|
|
||||||
|
|
||||||
interface IErrorReporter {
|
interface IErrorReporter {
|
||||||
fun err(msg: String, position: Position)
|
fun err(msg: String, position: Position)
|
||||||
@ -10,7 +7,6 @@ interface IErrorReporter {
|
|||||||
fun report()
|
fun report()
|
||||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
||||||
if(numErrors>0)
|
if(numErrors>0)
|
||||||
throw AbortCompilation("There are $numErrors errors and $numWarnings warnings.")
|
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compilerinterface
|
package prog8.code.core
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -10,18 +10,17 @@ interface IMachineFloat {
|
|||||||
|
|
||||||
enum class CpuType {
|
enum class CpuType {
|
||||||
CPU6502,
|
CPU6502,
|
||||||
CPU65c02
|
CPU65c02,
|
||||||
|
VIRTUAL
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMachineDefinition {
|
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 POINTER_MEM_SIZE: Int
|
val ESTACK_LO: UInt
|
||||||
val ESTACK_LO: Int
|
val ESTACK_HI: UInt
|
||||||
val ESTACK_HI: Int
|
val PROGRAM_LOAD_ADDRESS : UInt
|
||||||
val BASIC_LOAD_ADDRESS : Int
|
|
||||||
val RAW_LOAD_ADDRESS : Int
|
|
||||||
|
|
||||||
val opcodeNames: Set<String>
|
val opcodeNames: Set<String>
|
||||||
var zeropage: Zeropage
|
var zeropage: Zeropage
|
||||||
@ -32,5 +31,5 @@ interface IMachineDefinition {
|
|||||||
|
|
||||||
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 isRegularRAMaddress(address: Int): Boolean
|
fun isIOAddress(address: UInt): Boolean
|
||||||
}
|
}
|
6
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
6
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
interface IMemSizer {
|
||||||
|
fun memorySize(dt: DataType): Int
|
||||||
|
fun memorySize(arrayDt: DataType, numElements: Int): Int
|
||||||
|
}
|
14
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
14
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
enum class Encoding(val prefix: String) {
|
||||||
|
DEFAULT("default"), // depends on compilation target
|
||||||
|
PETSCII("petscii"), // c64/c128/cx16
|
||||||
|
SCREENCODES("sc"), // c64/c128/cx16
|
||||||
|
ATASCII("atascii"), // atari
|
||||||
|
ISO("iso") // cx16
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStringEncoding {
|
||||||
|
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
|
fun decodeString(bytes: List<UByte>, encoding: Encoding): String
|
||||||
|
}
|
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
|
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||||
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
|
val BitwiseOperators = setOf("&", "|", "^")
|
||||||
|
|
||||||
|
fun invertedComparisonOperator(operator: String) =
|
||||||
|
when (operator) {
|
||||||
|
"==" -> "!="
|
||||||
|
"!=" -> "=="
|
||||||
|
"<" -> ">="
|
||||||
|
">" -> "<="
|
||||||
|
"<=" -> ">"
|
||||||
|
">=" -> "<"
|
||||||
|
else -> null
|
||||||
|
}
|
16
codeCore/src/prog8/code/core/Position.kt
Normal file
16
codeCore/src/prog8/code/core/Position.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
|
||||||
|
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}]"
|
||||||
|
fun toClickableStr(): String {
|
||||||
|
val path = Path(file).absolute().normalize()
|
||||||
|
return "file://$path:$line:$startCol:"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DUMMY = Position("<dummy>", 0, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?)
|
@ -1,27 +1,20 @@
|
|||||||
package prog8.parser
|
package prog8.code.core
|
||||||
|
|
||||||
import org.antlr.v4.runtime.CharStream
|
|
||||||
import org.antlr.v4.runtime.CharStreams
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.channels.Channels
|
|
||||||
import java.nio.charset.CodingErrorAction
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
|
||||||
|
const val internedStringsModuleName = "prog8_interned_strings"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates - and ties together - actual source code (=text)
|
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
||||||
* and its [origin].
|
|
||||||
*/
|
*/
|
||||||
sealed class SourceCode {
|
sealed class SourceCode {
|
||||||
|
|
||||||
/**
|
|
||||||
* To be used *only* by the parser (as input to a TokenStream).
|
|
||||||
* DO NOT mess around with!
|
|
||||||
*/
|
|
||||||
internal abstract fun getCharStream(): CharStream
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this [SourceCode] instance was created as a [Resource]
|
* Whether this [SourceCode] instance was created as a [Resource]
|
||||||
*/
|
*/
|
||||||
@ -32,6 +25,11 @@ sealed class SourceCode {
|
|||||||
*/
|
*/
|
||||||
abstract val isFromFilesystem: Boolean
|
abstract val isFromFilesystem: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logical name of the source code unit. Usually the module's name.
|
||||||
|
*/
|
||||||
|
abstract val name: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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:
|
||||||
@ -44,11 +42,10 @@ sealed class SourceCode {
|
|||||||
/**
|
/**
|
||||||
* The source code as plain string.
|
* The source code as plain string.
|
||||||
*/
|
*/
|
||||||
abstract fun readText(): String
|
abstract val text: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deliberately does NOT return the actual text.
|
* Printable representation, deliberately does NOT return the actual text.
|
||||||
* For this - if at all - use [getCharStream].
|
|
||||||
*/
|
*/
|
||||||
final override fun toString() = "${this.javaClass.name}[${this.origin}]"
|
final override fun toString() = "${this.javaClass.name}[${this.origin}]"
|
||||||
|
|
||||||
@ -69,43 +66,40 @@ 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 `$stringSourcePrefix44c56085>`.
|
||||||
*/
|
*/
|
||||||
class Text(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 fun getCharStream(): CharStream = CharStreams.fromString(text, origin)
|
override val name = "<unnamed-text>"
|
||||||
override fun readText() = text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get [SourceCode] from the file represented by the specified Path.
|
* Get [SourceCode] from the file represented by the specified Path.
|
||||||
* This does not actually *access* the file, but it does check
|
* This immediately reads the file fully into memory.
|
||||||
* whether it
|
|
||||||
* * exists
|
|
||||||
* * is a regular file (ie: not a directory)
|
|
||||||
* * and is actually readable
|
|
||||||
*
|
*
|
||||||
* [origin] will be the given path in absolute and normalized form.
|
* [origin] will be the given path in absolute and normalized form.
|
||||||
* @throws NoSuchFileException if the file does not exist
|
* @throws NoSuchFileException if the file does not exist
|
||||||
* @throws AccessDeniedException if the given path points to a directory or the file is non-readable for some other reason
|
* @throws FileSystemException if the file cannot be read
|
||||||
*/
|
*/
|
||||||
class File(path: Path): SourceCode() {
|
class File(path: Path): SourceCode() {
|
||||||
private val normalized = path.normalize()
|
override val text: String
|
||||||
init {
|
override val origin: String
|
||||||
val file = normalized.toFile()
|
override val name: String
|
||||||
if (!path.exists())
|
|
||||||
throw NoSuchFileException(file)
|
|
||||||
if (path.isDirectory())
|
|
||||||
throw AccessDeniedException(file, reason = "Not a file but a directory")
|
|
||||||
if (!path.isReadable())
|
|
||||||
throw AccessDeniedException(file, reason = "Is not readable")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isFromResources = false
|
override val isFromResources = false
|
||||||
override val isFromFilesystem = true
|
override val isFromFilesystem = true
|
||||||
override val origin = relative(normalized).toString()
|
|
||||||
override fun getCharStream(): CharStream = CharStreams.fromPath(normalized)
|
init {
|
||||||
override fun readText() = normalized.readText()
|
val normalized = path.normalize()
|
||||||
|
origin = relative(normalized).toString()
|
||||||
|
try {
|
||||||
|
text = normalized.readText()
|
||||||
|
name = normalized.toFile().nameWithoutExtension
|
||||||
|
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||||
|
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||||
|
} catch (iox: IOException) {
|
||||||
|
throw FileSystemException(normalized.toFile()).also { it.initCause(iox) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,6 +108,12 @@ sealed class SourceCode {
|
|||||||
class Resource(pathString: String): SourceCode() {
|
class Resource(pathString: String): SourceCode() {
|
||||||
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
|
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
|
||||||
|
|
||||||
|
override val isFromResources = true
|
||||||
|
override val isFromFilesystem = false
|
||||||
|
override val origin = "$libraryFilePrefix$normalized"
|
||||||
|
override val text: String
|
||||||
|
override val name: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val rscURL = object {}.javaClass.getResource(normalized)
|
val rscURL = object {}.javaClass.getResource(normalized)
|
||||||
if (rscURL == null) {
|
if (rscURL == null) {
|
||||||
@ -123,32 +123,19 @@ sealed class SourceCode {
|
|||||||
reason = "looked in resources rooted at $rscRoot"
|
reason = "looked in resources rooted at $rscRoot"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val isFromResources = true
|
|
||||||
override val isFromFilesystem = false
|
|
||||||
override val origin = "$libraryFilePrefix$normalized"
|
|
||||||
public override fun getCharStream(): CharStream {
|
|
||||||
val inpStr = object {}.javaClass.getResourceAsStream(normalized)!!
|
|
||||||
// CharStreams.fromStream() doesn't allow us to set the stream name properly, so we use a lower level api
|
|
||||||
val channel = Channels.newChannel(inpStr)
|
|
||||||
return CharStreams.fromChannel(channel, StandardCharsets.UTF_8, 4096, CodingErrorAction.REPLACE, origin, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readText(): String {
|
|
||||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||||
return stream!!.bufferedReader().use { r -> r.readText() }
|
text = stream!!.reader().use { it.readText() }
|
||||||
|
name = Path.of(pathString).toFile().nameWithoutExtension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SourceCode for internally generated nodes (usually Modules)
|
* SourceCode for internally generated nodes (usually Modules)
|
||||||
*/
|
*/
|
||||||
class Generated(name: String) : SourceCode() {
|
class Generated(override val name: String) : SourceCode() {
|
||||||
override fun getCharStream(): CharStream = throw IOException("generated code nodes doesn't have a stream to read")
|
|
||||||
override val isFromResources: Boolean = false
|
override val isFromResources: Boolean = false
|
||||||
override val isFromFilesystem: Boolean = false
|
override val isFromFilesystem: Boolean = false
|
||||||
override val origin: String = name
|
override val origin: String = name
|
||||||
override fun readText() = throw IOException("generated code nodes don't have a text representation")
|
override val text: String = "<generated code node, no text representation>"
|
||||||
}
|
}
|
||||||
}
|
}
|
120
codeCore/src/prog8/code/core/Zeropage.kt
Normal file
120
codeCore/src/prog8/code/core/Zeropage.kt
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
|
||||||
|
|
||||||
|
class ZeropageAllocationError(message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||||
|
|
||||||
|
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_W1 : UInt // temp storage 1 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.
|
||||||
|
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||||
|
val allocatedVariables = mutableMapOf<List<String>, ZpAllocation>()
|
||||||
|
|
||||||
|
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
|
fun removeReservedFromFreePool() {
|
||||||
|
synchronized(this) {
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
|
||||||
|
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
fun hasByteAvailable() = if(options.zeropage== ZeropageType.DONTUSE) false else free.isNotEmpty()
|
||||||
|
fun hasWordAvailable(): Boolean {
|
||||||
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return free.windowed(2).any { it[0] == it[1] - 1u }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allocate(name: List<String>,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter
|
||||||
|
): Result<Pair<UInt, Int>, ZeropageAllocationError> {
|
||||||
|
|
||||||
|
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
||||||
|
|
||||||
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
|
return Err(ZeropageAllocationError("zero page usage has been disabled"))
|
||||||
|
|
||||||
|
val size: Int =
|
||||||
|
when (datatype) {
|
||||||
|
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||||
|
DataType.STR, in ArrayDatatypes -> {
|
||||||
|
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||||
|
if(position!=null)
|
||||||
|
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
|
||||||
|
else
|
||||||
|
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
|
||||||
|
memsize
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
if (options.floats) {
|
||||||
|
val memsize = options.compTarget.memorySize(DataType.FLOAT)
|
||||||
|
if(position!=null)
|
||||||
|
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||||
|
else
|
||||||
|
errors.warn("$name: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
|
||||||
|
memsize
|
||||||
|
} else return Err(ZeropageAllocationError("floating point option not enabled"))
|
||||||
|
}
|
||||||
|
else -> return Err(ZeropageAllocationError("cannot put datatype $datatype in zeropage"))
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
if(free.size > 0) {
|
||||||
|
if(size==1) {
|
||||||
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
|
if(oneSeparateByteFree(candidate))
|
||||||
|
return Ok(Pair(makeAllocation(candidate, 1, datatype, name), 1))
|
||||||
|
}
|
||||||
|
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 1))
|
||||||
|
}
|
||||||
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
|
if (sequentialFree(candidate, size))
|
||||||
|
return Ok(Pair(makeAllocation(candidate, size, datatype, name), size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(ZeropageAllocationError("no more free space in ZP to allocate $size sequential bytes"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reserve(range: UIntRange) = free.removeAll(range)
|
||||||
|
|
||||||
|
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>): UInt {
|
||||||
|
require(size>=0)
|
||||||
|
free.removeAll(address until address+size.toUInt())
|
||||||
|
if(name.isNotEmpty()) {
|
||||||
|
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
|
||||||
|
DataType.STR -> ZpAllocation(address, datatype, size)
|
||||||
|
in ArrayDatatypes -> ZpAllocation(address, datatype, size)
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun oneSeparateByteFree(address: UInt) = address in free && address-1u !in free && address+1u !in free
|
||||||
|
private fun sequentialFree(address: UInt, size: Int): Boolean {
|
||||||
|
require(size>0)
|
||||||
|
return free.containsAll((address until address+size.toUInt()).toList())
|
||||||
|
}
|
||||||
|
}
|
28
codeCore/src/prog8/code/target/AtariTarget.kt
Normal file
28
codeCore/src/prog8/code/target/AtariTarget.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.atari.AtariMachineDefinition
|
||||||
|
|
||||||
|
|
||||||
|
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||||
|
override val name = NAME
|
||||||
|
override val machine = AtariMachineDefinition()
|
||||||
|
override val supportedEncodings = setOf(Encoding.ATASCII)
|
||||||
|
override val defaultEncoding = Encoding.ATASCII
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "atari"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
return when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||||
|
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||||
|
else -> Int.MIN_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||||
|
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||||
|
}
|
20
codeCore/src/prog8/code/target/C128Target.kt
Normal file
20
codeCore/src/prog8/code/target/C128Target.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.target.c128.C128MachineDefinition
|
||||||
|
import prog8.code.target.cbm.CbmMemorySizer
|
||||||
|
|
||||||
|
|
||||||
|
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||||
|
override val name = NAME
|
||||||
|
override val machine = C128MachineDefinition()
|
||||||
|
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "c128"
|
||||||
|
}
|
||||||
|
}
|
20
codeCore/src/prog8/code/target/C64Target.kt
Normal file
20
codeCore/src/prog8/code/target/C64Target.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.target.c64.C64MachineDefinition
|
||||||
|
import prog8.code.target.cbm.CbmMemorySizer
|
||||||
|
|
||||||
|
|
||||||
|
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||||
|
override val name = NAME
|
||||||
|
override val machine = C64MachineDefinition()
|
||||||
|
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "c64"
|
||||||
|
}
|
||||||
|
}
|
20
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
20
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.target.cbm.CbmMemorySizer
|
||||||
|
import prog8.code.target.cx16.CX16MachineDefinition
|
||||||
|
|
||||||
|
|
||||||
|
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||||
|
override val name = NAME
|
||||||
|
override val machine = CX16MachineDefinition()
|
||||||
|
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "cx16"
|
||||||
|
}
|
||||||
|
}
|
39
codeCore/src/prog8/code/target/Encoder.kt
Normal file
39
codeCore/src/prog8/code/target/Encoder.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.target.cbm.AtasciiEncoding
|
||||||
|
import prog8.code.target.cbm.IsoEncoding
|
||||||
|
import prog8.code.target.cbm.PetsciiEncoding
|
||||||
|
|
||||||
|
|
||||||
|
object Encoder: IStringEncoding {
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
|
val coded = when(encoding) {
|
||||||
|
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||||
|
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||||
|
Encoding.ISO -> IsoEncoding.encode(str)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||||
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
|
}
|
||||||
|
return coded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
||||||
|
val decoded = when(encoding) {
|
||||||
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
|
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||||
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
|
}
|
||||||
|
return decoded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
27
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
27
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||||
|
|
||||||
|
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||||
|
override val name = NAME
|
||||||
|
override val machine = VirtualMachineDefinition()
|
||||||
|
override val supportedEncodings = setOf(Encoding.ISO)
|
||||||
|
override val defaultEncoding = Encoding.ISO
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "virtual"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
return when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||||
|
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||||
|
else -> Int.MIN_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||||
|
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package prog8.code.target.atari
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.c64.normal6502instructions
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class AtariMachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||||
|
override val FLOAT_MEM_SIZE = 6
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||||
|
|
||||||
|
// 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 val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = TODO("float from number")
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return if (compilerOptions.output == OutputType.XEX)
|
||||||
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
val emulatorName: String
|
||||||
|
val cmdline: List<String>
|
||||||
|
when(selectedEmulator) {
|
||||||
|
1 -> {
|
||||||
|
emulatorName = "atari800"
|
||||||
|
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
emulatorName = "altirra"
|
||||||
|
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("Atari target only supports atari800 and altirra emulators.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO monlist?
|
||||||
|
|
||||||
|
println("\nStarting Atari800XL emulator $emulatorName...")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = AtariZeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val opcodeNames = normal6502instructions
|
||||||
|
}
|
45
codeCore/src/prog8/code/target/atari/AtariZeropage.kt
Normal file
45
codeCore/src/prog8/code/target/atari/AtariZeropage.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package prog8.code.target.atari
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
||||||
|
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?
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
||||||
|
free.addAll(0x00u..0xffu)
|
||||||
|
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x80u..0xffu) // TODO
|
||||||
|
}
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.FLOATSAFE -> {
|
||||||
|
free.addAll(0x80u..0xffu) // TODO
|
||||||
|
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
}
|
||||||
|
}
|
55
codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
Normal file
55
codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package prog8.code.target.c128
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.c64.normal6502instructions
|
||||||
|
import prog8.code.target.cbm.Mflpt5
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class C128MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||||
|
|
||||||
|
// 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 val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("\nStarting C-128 emulator x128...")
|
||||||
|
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C128Zeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val opcodeNames = normal6502instructions
|
||||||
|
}
|
43
codeCore/src/prog8/code/target/c128/C128Zeropage.kt
Normal file
43
codeCore/src/prog8/code/target/c128/C128Zeropage.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.code.target.c128
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
|
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
|
||||||
|
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
|
||||||
|
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE,
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.clear() // TODO c128 usable zero page addresses
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
}
|
||||||
|
}
|
76
codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
Normal file
76
codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package prog8.code.target.c64
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.cbm.Mflpt5
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
|
// 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 val ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(x: IOException) {
|
||||||
|
continue // try the next emulator executable
|
||||||
|
}
|
||||||
|
process.waitFor()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C64Zeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
74
codeCore/src/prog8/code/target/c64/C64Zeropage.kt
Normal file
74
codeCore/src/prog8/code/target/c64/C64Zeropage.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package prog8.code.target.c64
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
|
free.addAll(0x02u..0xffu)
|
||||||
|
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||||
|
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||||
|
} else {
|
||||||
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
free.addAll(listOf(
|
||||||
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||||
|
0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||||
|
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||||
|
0x22, 0x23, 0x24, 0x25,
|
||||||
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||||
|
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
|
// 0x90-0xfa is 'kernal work storage area'
|
||||||
|
).map{it.toUInt()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
|
free.removeAll(listOf(
|
||||||
|
0x22, 0x23, 0x24, 0x25,
|
||||||
|
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
|
).map{it.toUInt()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||||
|
// 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*
|
||||||
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
|
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||||
|
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||||
|
} else {
|
||||||
|
// don't use the zeropage at all
|
||||||
|
free.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
}
|
||||||
|
}
|
214
codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt
Normal file
214
codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
|
||||||
|
object AtasciiEncoding {
|
||||||
|
|
||||||
|
private val decodeTable: CharArray = charArrayOf(
|
||||||
|
// $00
|
||||||
|
'♥',
|
||||||
|
'├',
|
||||||
|
'\uf130', // 🮇 0x02 -> RIGHT ONE QUARTER BLOCK (CUS)
|
||||||
|
'┘',
|
||||||
|
'┤',
|
||||||
|
'┐',
|
||||||
|
'╱',
|
||||||
|
'╲',
|
||||||
|
'◢',
|
||||||
|
'▗',
|
||||||
|
'◣',
|
||||||
|
'▝',
|
||||||
|
'▘',
|
||||||
|
'\uf132', // 🮂 0x1d -> UPPER ONE QUARTER BLOCK (CUS)
|
||||||
|
'▂',
|
||||||
|
'▖',
|
||||||
|
|
||||||
|
// $10
|
||||||
|
'♣',
|
||||||
|
'┌',
|
||||||
|
'─',
|
||||||
|
'┼',
|
||||||
|
'•',
|
||||||
|
'▄',
|
||||||
|
'▎',
|
||||||
|
'┬',
|
||||||
|
'┴',
|
||||||
|
'▌',
|
||||||
|
'└',
|
||||||
|
'\u001b', // $1b = escape
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1c = cursor up
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1d = cursor down
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1e = cursor left
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1f = cursor right
|
||||||
|
|
||||||
|
// $20
|
||||||
|
' ',
|
||||||
|
'!',
|
||||||
|
'"',
|
||||||
|
'#',
|
||||||
|
'$',
|
||||||
|
'%',
|
||||||
|
'&',
|
||||||
|
'\'',
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
|
'*',
|
||||||
|
'+',
|
||||||
|
',',
|
||||||
|
'-',
|
||||||
|
'.',
|
||||||
|
'/',
|
||||||
|
|
||||||
|
// $30
|
||||||
|
'0',
|
||||||
|
'1',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4',
|
||||||
|
'5',
|
||||||
|
'6',
|
||||||
|
'7',
|
||||||
|
'8',
|
||||||
|
'9',
|
||||||
|
':',
|
||||||
|
';',
|
||||||
|
'<',
|
||||||
|
'=',
|
||||||
|
'>',
|
||||||
|
'?',
|
||||||
|
|
||||||
|
// $40
|
||||||
|
'@',
|
||||||
|
'A',
|
||||||
|
'B',
|
||||||
|
'C',
|
||||||
|
'D',
|
||||||
|
'E',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'I',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'M',
|
||||||
|
'N',
|
||||||
|
'O',
|
||||||
|
|
||||||
|
// $50
|
||||||
|
'P',
|
||||||
|
'Q',
|
||||||
|
'R',
|
||||||
|
'S',
|
||||||
|
'T',
|
||||||
|
'U',
|
||||||
|
'V',
|
||||||
|
'W',
|
||||||
|
'X',
|
||||||
|
'Y',
|
||||||
|
'Z',
|
||||||
|
'[',
|
||||||
|
'\\',
|
||||||
|
']',
|
||||||
|
'^',
|
||||||
|
'_',
|
||||||
|
|
||||||
|
// $60
|
||||||
|
'♦',
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
'e',
|
||||||
|
'f',
|
||||||
|
'g',
|
||||||
|
'h',
|
||||||
|
'i',
|
||||||
|
'j',
|
||||||
|
'k',
|
||||||
|
'l',
|
||||||
|
'm',
|
||||||
|
'n',
|
||||||
|
'o',
|
||||||
|
|
||||||
|
// $70
|
||||||
|
'p',
|
||||||
|
'q',
|
||||||
|
'r',
|
||||||
|
's',
|
||||||
|
't',
|
||||||
|
'u',
|
||||||
|
'v',
|
||||||
|
'w',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'z',
|
||||||
|
'♠',
|
||||||
|
'|',
|
||||||
|
'\u000c', // $7d -> FORM FEED (CLEAR SCREEN)
|
||||||
|
'\u0008', // $7e -> BACKSPACE
|
||||||
|
'\u0009', // $7f -> TAB
|
||||||
|
|
||||||
|
// $80-$ff are reversed video characters + various special characters.
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $90
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\n', // $9b -> EOL/RETURN
|
||||||
|
'\ufffe', // UNDEFINED $9c = DELETE LINE
|
||||||
|
'\ufffe', // UNDEFINED $9d = INSERT LINE
|
||||||
|
'\ufffe', // UNDEFINED $9e = CLEAR TAB STOP
|
||||||
|
'\ufffe', // UNDEFINED $9f = SET TAB STOP
|
||||||
|
// $a0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $b0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $c0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $d0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $e0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $f0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\u0007', // $fd = bell/beep
|
||||||
|
'\u007f', // $fe = DELETE
|
||||||
|
'\ufffe' // UNDEFINED $ff = INSERT
|
||||||
|
)
|
||||||
|
|
||||||
|
private val encodeTable = decodeTable.withIndex().associate{it.value to it.index}
|
||||||
|
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> encodeTable.getValue(chr).toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(mapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
||||||
|
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
||||||
|
}
|
||||||
|
}
|
18
codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
Normal file
18
codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
internal object CbmMemorySizer: IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
return when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||||
|
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
else -> Int.MIN_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||||
|
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||||
|
}
|
37
codeCore/src/prog8/code/target/cbm/IsoEncoding.kt
Normal file
37
codeCore/src/prog8/code/target/cbm/IsoEncoding.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
object IsoEncoding {
|
||||||
|
val charset: Charset = Charset.forName("ISO-8859-15")
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> charset.encode(chr.toString())[0].toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(mapped)
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
codeCore/src/prog8/code/target/cbm/Mflpt5.kt
Normal file
78
codeCore/src/prog8/code/target/cbm/Mflpt5.kt
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
|
import prog8.code.core.IMachineFloat
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
|
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte): IMachineFloat {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
|
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
|
const val FLOAT_MEM_SIZE = 5
|
||||||
|
|
||||||
|
val zero = Mflpt5(0u, 0u, 0u, 0u, 0u)
|
||||||
|
fun fromNumber(num: Number): Mflpt5 {
|
||||||
|
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||||
|
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||||
|
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||||
|
|
||||||
|
val flt = num.toDouble()
|
||||||
|
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||||
|
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||||
|
if (flt == 0.0)
|
||||||
|
return zero
|
||||||
|
|
||||||
|
val sign = if (flt < 0.0) 0x80L else 0x00L
|
||||||
|
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
||||||
|
var mantissa = flt.absoluteValue
|
||||||
|
|
||||||
|
// if mantissa is too large, shift right and adjust exponent
|
||||||
|
while (mantissa >= 0x100000000) {
|
||||||
|
mantissa /= 2.0
|
||||||
|
exponent++
|
||||||
|
}
|
||||||
|
// if mantissa is too small, shift left and adjust exponent
|
||||||
|
while (mantissa < 0x80000000) {
|
||||||
|
mantissa *= 2.0
|
||||||
|
exponent--
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
exponent < 0 -> zero // underflow, use zero instead
|
||||||
|
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||||
|
exponent == 0 -> zero
|
||||||
|
else -> {
|
||||||
|
val mantLong = mantissa.toLong()
|
||||||
|
Mflpt5(
|
||||||
|
exponent.toUByte(),
|
||||||
|
(mantLong.and(0x7f000000L) ushr 24).or(sign).toUByte(),
|
||||||
|
(mantLong.and(0x00ff0000L) ushr 16).toUByte(),
|
||||||
|
(mantLong.and(0x0000ff00L) ushr 8).toUByte(),
|
||||||
|
(mantLong.and(0x000000ffL)).toUByte()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toDouble(): Double {
|
||||||
|
if (this == zero) return 0.0
|
||||||
|
val exp = b0.toInt() - 128
|
||||||
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
|
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
||||||
|
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||||
|
return if (sign) -result else result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun makeFloatFillAsm(): String {
|
||||||
|
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||||
|
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||||
|
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||||
|
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||||
|
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||||
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,14 @@
|
|||||||
package prog8.compiler.target.cbm
|
package prog8.code.target.cbm
|
||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
import com.github.michaelbull.result.Err
|
||||||
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 prog8.ast.antlr.escape
|
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
|
|
||||||
object Petscii {
|
object PetsciiEncoding {
|
||||||
|
|
||||||
// decoding: from Petscii/Screencodes (0-255) to unicode
|
// decoding: from Petscii/Screencodes (0-255) to unicode
|
||||||
// character tables used from https://github.com/dj51d/cbmcodecs
|
// character tables used from https://github.com/irmen/cbmcodecs2
|
||||||
|
|
||||||
private val decodingPetsciiLowercase = charArrayOf(
|
private val decodingPetsciiLowercase = charArrayOf(
|
||||||
'\u0000', // 0x00 -> \u0000
|
'\u0000', // 0x00 -> \u0000
|
||||||
@ -159,7 +158,7 @@ object Petscii {
|
|||||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||||
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
||||||
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
||||||
'\u000c', // 0x93 -> FORM FEED
|
'\u000c', // 0x93 -> FORM FEED (CLEAR SCREEN)
|
||||||
'\uf121', // 0x94 -> INSERT (CUS)
|
'\uf121', // 0x94 -> INSERT (CUS)
|
||||||
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
||||||
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
||||||
@ -195,7 +194,7 @@ object Petscii {
|
|||||||
'\u258e', // ▎ 0xB4 -> LEFT ONE QUARTER BLOCK
|
'\u258e', // ▎ 0xB4 -> LEFT ONE QUARTER BLOCK
|
||||||
'\u258d', // ▍ 0xB5 -> LEFT THREE EIGTHS BLOCK
|
'\u258d', // ▍ 0xB5 -> LEFT THREE EIGTHS BLOCK
|
||||||
'\uf131', // 0xB6 -> RIGHT THREE EIGHTHS BLOCK (CUS)
|
'\uf131', // 0xB6 -> RIGHT THREE EIGHTHS BLOCK (CUS)
|
||||||
'\uf132', // 0xB7 -> UPPER ONE QUARTER BLOCK (CUS)
|
'\uf132', // 🮂 0xB7 -> UPPER ONE QUARTER BLOCK (CUS)
|
||||||
'\uf133', // 0xB8 -> UPPER THREE EIGHTS BLOCK (CUS)
|
'\uf133', // 0xB8 -> UPPER THREE EIGHTS BLOCK (CUS)
|
||||||
'\u2583', // ▃ 0xB9 -> LOWER THREE EIGHTHS BLOCK
|
'\u2583', // ▃ 0xB9 -> LOWER THREE EIGHTHS BLOCK
|
||||||
'\u2713', // ✓ 0xBA -> CHECK MARK
|
'\u2713', // ✓ 0xBA -> CHECK MARK
|
||||||
@ -246,7 +245,7 @@ object Petscii {
|
|||||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
'\uf12f', // 0xE8 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
|
'\uf12f', // 0xE8 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
|
||||||
'\uf13a', // 0xE9 -> MEDIUM SHADE SLASHED RIGHT (CUS)
|
'\uf13a', // 0xE9 -> MEDIUM SHADE SLASHED RIGHT (CUS)
|
||||||
'\uf130', // 0xEA -> RIGHT ONE QUARTER BLOCK (CUS)
|
'\uf130', // 🮇 0xEA -> RIGHT ONE QUARTER BLOCK (CUS)
|
||||||
'\u251c', // ├ 0xEB -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
'\u251c', // ├ 0xEB -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||||
'\u2597', // ▗ 0xEC -> QUADRANT LOWER RIGHT
|
'\u2597', // ▗ 0xEC -> QUADRANT LOWER RIGHT
|
||||||
'\u2514', // └ 0xED -> BOX DRAWINGS LIGHT UP AND RIGHT
|
'\u2514', // └ 0xED -> BOX DRAWINGS LIGHT UP AND RIGHT
|
||||||
@ -418,7 +417,7 @@ object Petscii {
|
|||||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||||
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
||||||
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
||||||
'\u000c', // 0x93 -> FORM FEED
|
'\u000c', // 0x93 -> FORM FEED (CLEAR SCREEN)
|
||||||
'\uf121', // 0x94 -> INSERT (CUS)
|
'\uf121', // 0x94 -> INSERT (CUS)
|
||||||
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
||||||
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
||||||
@ -1065,19 +1064,19 @@ object Petscii {
|
|||||||
else -> chr
|
else -> chr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodePetscii(text: String, lowercase: Boolean = false): Result<List<Short>, CharConversionException> {
|
fun encodePetscii(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
val chr = replaceSpecial(chr3)
|
val chr = replaceSpecial(chr3)
|
||||||
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
||||||
return screencode?.toShort() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toShort()
|
(chr.code - 0x8000).toUByte()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}Petscii character for '${escape(chr.toString())}' (${chr.code})")
|
throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1095,28 +1094,32 @@ object Petscii {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodePetscii(petscii: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
return petscii.map {
|
return try {
|
||||||
val code = it.toInt()
|
Ok(petscii.map {
|
||||||
if(code<0 || code>= decodingPetsciiLowercase.size)
|
val code = it.toInt()
|
||||||
throw CharConversionException("petscii $code out of range 0..${decodingPetsciiLowercase.size-1}")
|
if(code<0 || code>= decodingPetsciiLowercase.size)
|
||||||
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
throw CharConversionException("petscii $code out of range 0..${decodingPetsciiLowercase.size-1}")
|
||||||
}.joinToString("")
|
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
||||||
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
return Err(ce)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeScreencode(text: String, lowercase: Boolean = false): Result<List<Short>, CharConversionException> {
|
fun encodeScreencode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
val chr = replaceSpecial(chr3)
|
val chr = replaceSpecial(chr3)
|
||||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
return screencode?.toShort() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toShort()
|
(chr.code - 0x8000).toUByte()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}Screencode character for '${escape(chr.toString())}' (${chr.code})")
|
throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1134,47 +1137,50 @@ object Petscii {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodeScreencode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
return screencode.map {
|
return try {
|
||||||
val code = it.toInt()
|
Ok(screencode.map {
|
||||||
if(code<0 || code>= decodingScreencodeLowercase.size)
|
val code = it.toInt()
|
||||||
throw CharConversionException("screencode $code out of range 0..${decodingScreencodeLowercase.size-1}")
|
if(code<0 || code>= decodingScreencodeLowercase.size)
|
||||||
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
throw CharConversionException("screencode $code out of range 0..${decodingScreencodeLowercase.size-1}")
|
||||||
}.joinToString("")
|
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
||||||
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Result<Short, CharConversionException> {
|
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
||||||
val code = when {
|
val code: UInt = when {
|
||||||
petscii_code < 0 -> return Err(CharConversionException("petscii code out of range"))
|
petscii_code <= 0x1fu -> petscii_code + 128u
|
||||||
petscii_code <= 0x1f -> petscii_code + 128
|
petscii_code <= 0x3fu -> petscii_code.toUInt()
|
||||||
petscii_code <= 0x3f -> petscii_code.toInt()
|
petscii_code <= 0x5fu -> petscii_code - 64u
|
||||||
petscii_code <= 0x5f -> petscii_code - 64
|
petscii_code <= 0x7fu -> petscii_code - 32u
|
||||||
petscii_code <= 0x7f -> petscii_code - 32
|
petscii_code <= 0x9fu -> petscii_code + 64u
|
||||||
petscii_code <= 0x9f -> petscii_code + 64
|
petscii_code <= 0xbfu -> petscii_code - 64u
|
||||||
petscii_code <= 0xbf -> petscii_code - 64
|
petscii_code <= 0xfeu -> petscii_code - 128u
|
||||||
petscii_code <= 0xfe -> petscii_code - 128
|
petscii_code == 255.toUByte() -> 95u
|
||||||
petscii_code == 255.toShort() -> 95
|
|
||||||
else -> return Err(CharConversionException("petscii code out of range"))
|
else -> return Err(CharConversionException("petscii code out of range"))
|
||||||
}
|
}
|
||||||
if(inverseVideo)
|
if(inverseVideo) {
|
||||||
return Ok((code or 0x80).toShort())
|
return Ok((code or 0x80u).toUByte())
|
||||||
return Ok(code.toShort())
|
}
|
||||||
|
return Ok(code.toUByte())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun scr2petscii(screencode: Short): Result<Short, CharConversionException> {
|
fun scr2petscii(screencode: UByte): Result<UByte, CharConversionException> {
|
||||||
val petscii = when {
|
val petscii: UInt = when {
|
||||||
screencode < 0 -> return Err(CharConversionException("screencode out of range"))
|
screencode <= 0x1fu -> screencode + 64u
|
||||||
screencode <= 0x1f -> screencode + 64
|
screencode <= 0x3fu -> screencode.toUInt()
|
||||||
screencode <= 0x3f -> screencode.toInt()
|
screencode <= 0x5du -> screencode +123u
|
||||||
screencode <= 0x5d -> screencode +123
|
screencode == 0x5e.toUByte() -> 255u
|
||||||
screencode == 0x5e.toShort() -> 255
|
screencode == 0x5f.toUByte() -> 223u
|
||||||
screencode == 0x5f.toShort() -> 223
|
screencode <= 0x7fu -> screencode + 64u
|
||||||
screencode <= 0x7f -> screencode + 64
|
screencode <= 0xbfu -> screencode - 128u
|
||||||
screencode <= 0xbf -> screencode - 128
|
screencode <= 0xfeu -> screencode - 64u
|
||||||
screencode <= 0xfe -> screencode - 64
|
screencode == 255.toUByte() -> 191u
|
||||||
screencode == 255.toShort() -> 191
|
|
||||||
else -> return Err(CharConversionException("screencode out of range"))
|
else -> return Err(CharConversionException("screencode out of range"))
|
||||||
}
|
}
|
||||||
return Ok(petscii.toShort())
|
return Ok(petscii.toUByte())
|
||||||
}
|
}
|
||||||
}
|
}
|
77
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
77
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.cbm.Mflpt5
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class CX16MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU65c02
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
|
// 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 val ESTACK_HI = 0x0500u // $0500-$05ff inclusive
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
val emulator: String
|
||||||
|
val extraArgs: List<String>
|
||||||
|
|
||||||
|
when(selectedEmulator) {
|
||||||
|
1 -> {
|
||||||
|
emulator = "x16emu"
|
||||||
|
extraArgs = emptyList()
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
emulator = "box16"
|
||||||
|
extraArgs = listOf("-sym", viceMonListName(programNameWithPath.toString()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
60
codeCore/src/prog8/code/target/cx16/CX16Zeropage.kt
Normal file
60
codeCore/src/prog8/code/target/cx16/CX16Zeropage.kt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||||
|
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
free.addAll(0x22u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
free.addAll(0xa9u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.FLOATSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
free.addAll(0xd4u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
|
}
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package prog8.code.target.virtual
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.CpuType
|
||||||
|
import prog8.code.core.IMachineDefinition
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.VIRTUAL
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
|
|
||||||
|
override val ESTACK_LO = 0u // not actually used
|
||||||
|
override val ESTACK_HI = 0u // not actually used
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = TODO("float from number")
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return listOf("syslib")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
println("\nStarting Virtual Machine...")
|
||||||
|
// to not have external module dependencies we launch the virtual machine via reflection
|
||||||
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
|
val source = File("$programNameWithPath.p8virt").readText()
|
||||||
|
vm.runProgram(source, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {}
|
||||||
|
|
||||||
|
override val opcodeNames = emptySet<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(source: String, throttle: Boolean)
|
||||||
|
}
|
@ -11,17 +11,25 @@ java {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':compilerInterfaces')
|
implementation project(':codeAst')
|
||||||
|
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.12"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||||
|
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
|
||||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,23 +42,6 @@ sourceSets {
|
|||||||
srcDirs = ["${project.projectDir}/res"]
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
test {
|
|
||||||
java {
|
|
||||||
srcDirs = ["${project.projectDir}/test"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
||||||
test {
|
|
||||||
// Enable JUnit 5 (Gradle 4.6+).
|
|
||||||
useJUnitPlatform()
|
|
||||||
|
|
||||||
// Always run tests, even when nothing changed.
|
|
||||||
dependsOn 'cleanTest'
|
|
||||||
|
|
||||||
// Show test results.
|
|
||||||
testLogging {
|
|
||||||
events "skipped", "failed"
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,16 +4,14 @@
|
|||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="module" module-name="codeAst" />
|
||||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="compilerAst" />
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="junit.jupiter" level="project" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
3066
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
3066
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
452
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
452
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.ast.statements.VarDeclType
|
||||||
|
import prog8.code.core.IMachineDefinition
|
||||||
|
|
||||||
|
|
||||||
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
|
||||||
|
|
||||||
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
|
var linesByFour = getLinesBy(lines, 4)
|
||||||
|
|
||||||
|
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeIncDec(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeCmpSequence(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeStoreLoadSame(linesByFour, machine, program)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods= optimizeJsrRts(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
var linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
mods = optimizeSameAssignments(linesByFourteen, machine, program)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO more assembly optimizations
|
||||||
|
|
||||||
|
return numberOfOptimizations
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.isBranch() = this.startsWith("b")
|
||||||
|
private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty") || this.startsWith("stx")
|
||||||
|
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||||
|
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||||
|
|
||||||
|
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||||
|
|
||||||
|
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||||
|
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||||
|
if(modification.remove)
|
||||||
|
lines.removeAt(modification.lineIndex)
|
||||||
|
else
|
||||||
|
lines[modification.lineIndex] = modification.replacement!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||||
|
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||||
|
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
|
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// when statement (on bytes) generates a sequence of:
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$20
|
||||||
|
// beq check_prog8_s72choice_32
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$21
|
||||||
|
// beq check_prog8_s73choice_33
|
||||||
|
// the repeated lda can be removed
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||||
|
lines[1].value.trim().startsWith("cmp ") &&
|
||||||
|
lines[2].value.trim().startsWith("beq ") &&
|
||||||
|
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||||
|
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||||
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||||
|
lines[1].value.trim()=="dex" &&
|
||||||
|
lines[2].value.trim()=="inx" &&
|
||||||
|
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||||
|
|
||||||
|
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
||||||
|
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||||
|
// The better place to do this is in the Compiler instead and never create these types of assembly, but hey
|
||||||
|
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFourteen) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
val fourth = lines[3].value.trimStart()
|
||||||
|
val fifth = lines[4].value.trimStart()
|
||||||
|
val sixth = lines[5].value.trimStart()
|
||||||
|
val seventh = lines[6].value.trimStart()
|
||||||
|
val eighth = lines[7].value.trimStart()
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
|
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = fifth.substring(4)
|
||||||
|
val fourthvalue = sixth.substring(4)
|
||||||
|
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
|
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||||
|
val address1 = getAddressArg(first, program)
|
||||||
|
val address2 = getAddressArg(second, program)
|
||||||
|
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
|
||||||
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
|
mods.add(Modification(lines[5].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = third.substring(4)
|
||||||
|
if(firstvalue==secondvalue) {
|
||||||
|
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
||||||
|
val address = getAddressArg(first, program)
|
||||||
|
if(address==null || !machine.isIOAddress(address))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
|
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||||
|
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
|
val nineth = lines[8].value.trimStart()
|
||||||
|
val tenth = lines[9].value.trimStart()
|
||||||
|
val eleventh = lines[10].value.trimStart()
|
||||||
|
val twelveth = lines[11].value.trimStart()
|
||||||
|
val thirteenth = lines[12].value.trimStart()
|
||||||
|
val fourteenth = lines[13].value.trimStart()
|
||||||
|
|
||||||
|
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||||
|
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||||
|
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
|
// identical float init
|
||||||
|
mods.add(Modification(lines[7].index, true, null))
|
||||||
|
mods.add(Modification(lines[8].index, true, null))
|
||||||
|
mods.add(Modification(lines[9].index, true, null))
|
||||||
|
mods.add(Modification(lines[10].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var overlappingMods = false
|
||||||
|
/*
|
||||||
|
sta prog8_lib.retval_intermX ; remove
|
||||||
|
sty prog8_lib.retval_intermY ; remove
|
||||||
|
lda prog8_lib.retval_intermX ; remove
|
||||||
|
ldy prog8_lib.retval_intermY ; remove
|
||||||
|
sta A1
|
||||||
|
sty A2
|
||||||
|
*/
|
||||||
|
if(first.isStoreReg() && second.isStoreReg()
|
||||||
|
&& third.isLoadReg() && fourth.isLoadReg()
|
||||||
|
&& fifth.isStoreReg() && sixth.isStoreReg()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg2 = second[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
val reg4 = fourth[2]
|
||||||
|
val reg5 = fifth[2]
|
||||||
|
val reg6 = sixth[2]
|
||||||
|
if (reg1 == reg3 && reg1 == reg5 && reg2 == reg4 && reg2 == reg6) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
if(firstvalue.contains("prog8_lib.retval_interm") && secondvalue.contains("prog8_lib.retval_interm")
|
||||||
|
&& firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
overlappingMods = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
sty A2
|
||||||
|
lda A1 ; can be removed
|
||||||
|
ldy A2 ; can be removed if not followed by a branch instuction
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.isStoreReg() && second.isStoreReg()
|
||||||
|
&& third.isLoadReg() && fourth.isLoadReg()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg2 = second[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
val reg4 = fourth[2]
|
||||||
|
if(reg1==reg3 && reg2==reg4) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||||
|
val address = getAddressArg(first, program)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
if (!fifth.startsWith('b'))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
sty A2 ; ... or stz
|
||||||
|
lda A1 ; can be removed if not followed by a branch instruction
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.isStoreReg() && second.isStoreRegOrZero()
|
||||||
|
&& third.isLoadReg() && !fourth.isBranch()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
if(reg1==reg3) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
if(firstvalue==thirdvalue) {
|
||||||
|
val address = getAddressArg(first, program)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
ldy A1 ; make tay
|
||||||
|
sta A1 ; remove
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.startsWith("sta") && second.isLoadReg()
|
||||||
|
&& third.startsWith("sta") && second.length>4) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||||
|
val address = getAddressArg(first, program)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
val reg2 = second[2]
|
||||||
|
mods.add(Modification(lines[1].index, false, " ta$reg2"))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A ; or stz double store, remove this first one
|
||||||
|
sta A ; or stz
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
|
||||||
|
if(first[2]==second[2]) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
if(firstvalue==secondvalue) {
|
||||||
|
val address = getAddressArg(first, program)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[1].value.trimStart()
|
||||||
|
val second = lines[2].value.trimStart()
|
||||||
|
|
||||||
|
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
||||||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
|
||||||
|
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||||
|
) {
|
||||||
|
val third = lines[3].value.trimStart()
|
||||||
|
val attemptRemove =
|
||||||
|
if(third.isBranch()) {
|
||||||
|
// a branch instruction follows, we can only remove the load instruction if
|
||||||
|
// another load instruction of the same register precedes the store instruction
|
||||||
|
// (otherwise wrong cpu flags are used)
|
||||||
|
val loadinstruction = second.substring(0, 3)
|
||||||
|
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// no branch instruction follows, we can remove the load instruction
|
||||||
|
val address = getAddressArg(lines[2].value, program)
|
||||||
|
address==null || !machine.isIOAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attemptRemove) {
|
||||||
|
val firstLoc = first.substring(4).trimStart()
|
||||||
|
val secondLoc = second.substring(4).trimStart()
|
||||||
|
if (firstLoc == secondLoc)
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(first=="pha" && second=="pla" ||
|
||||||
|
first=="phx" && second=="plx" ||
|
||||||
|
first=="phy" && second=="ply" ||
|
||||||
|
first=="php" && second=="plp") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
} else if(first=="pha" && second=="plx") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tax"))
|
||||||
|
} else if(first=="pha" && second=="ply") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tay"))
|
||||||
|
} else if(first=="phx" && second=="pla") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " txa"))
|
||||||
|
} else if(first=="phx" && second=="ply") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " txy"))
|
||||||
|
} else if(first=="phy" && second=="pla") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tya"))
|
||||||
|
} else if(first=="phy" && second=="plx") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tyx"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||||
|
|
||||||
|
private fun getAddressArg(line: String, program: Program): UInt? {
|
||||||
|
val loadArg = line.trimStart().substring(3).trim()
|
||||||
|
return when {
|
||||||
|
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
||||||
|
loadArg.startsWith('%') -> loadArg.substring(1).toUIntOrNull(2)
|
||||||
|
loadArg.startsWith('#') -> null
|
||||||
|
loadArg.startsWith('(') -> null
|
||||||
|
loadArg[0].isLetter() -> {
|
||||||
|
val identMatch = identifierRegex.find(loadArg)
|
||||||
|
if(identMatch!=null) {
|
||||||
|
val identifier = identMatch.value
|
||||||
|
val decl = program.toplevelModule.lookup(identifier.split(".")) as? VarDecl
|
||||||
|
if(decl!=null) {
|
||||||
|
when(decl.type){
|
||||||
|
VarDeclType.VAR -> null
|
||||||
|
VarDeclType.CONST,
|
||||||
|
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else null
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
else -> loadArg.substring(1).toUIntOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[0].value
|
||||||
|
val second = lines[1].value
|
||||||
|
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
||||||
|
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
||||||
|
|| (" ina" in first || "\tina" in first) && (" dea" in second || "\tdea" in second)
|
||||||
|
|| (" inc a" in first || "\tinc a" in first) && (" dec a" in second || "\tdec a" in second)
|
||||||
|
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
||||||
|
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)
|
||||||
|
|| (" dea" in first || "\tdea" in first) && (" ina" in second || "\tina" in second)
|
||||||
|
|| (" dec a" in first || "\tdec a" in first) && (" inc a" in second || "\tinc a" in second)) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[0].value
|
||||||
|
val second = lines[1].value
|
||||||
|
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||||
|
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
61
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
61
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.ast.expressions.ArrayIndexedExpression
|
||||||
|
import prog8.ast.expressions.BuiltinFunctionCall
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.code.core.Cx16VirtualRegisters
|
||||||
|
import prog8.code.core.RegisterOrPair
|
||||||
|
import prog8.code.core.RegisterOrStatusflag
|
||||||
|
|
||||||
|
|
||||||
|
fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
|
||||||
|
val order = mutableListOf<Int>()
|
||||||
|
// order is:
|
||||||
|
// 1) cx16 virtual word registers,
|
||||||
|
// 2) paired CPU registers,
|
||||||
|
// 3) single CPU registers (X last), except A,
|
||||||
|
// 4) CPU Carry status flag
|
||||||
|
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
|
||||||
|
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
|
||||||
|
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
|
||||||
|
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
|
val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters }
|
||||||
|
val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A }
|
||||||
|
val (regA, rest) = args4.partition { it.value.second.registerOrPair != null }
|
||||||
|
|
||||||
|
cx16regs.forEach { order += it.index }
|
||||||
|
pairedRegs.forEach { order += it.index }
|
||||||
|
regsWithoutA.forEach {
|
||||||
|
if(it.value.second.registerOrPair != RegisterOrPair.X)
|
||||||
|
order += it.index
|
||||||
|
}
|
||||||
|
regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
|
||||||
|
rest.forEach { order += it.index }
|
||||||
|
regA.forEach { order += it.index }
|
||||||
|
require(order.size==sub.parameters.size)
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>,
|
||||||
|
paramRegisters: List<RegisterOrStatusflag>): Boolean {
|
||||||
|
fun isClobberRisk(expr: Expression): Boolean {
|
||||||
|
when (expr) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
return paramRegisters.any {
|
||||||
|
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BuiltinFunctionCall -> {
|
||||||
|
if (expr.name == "lsb" || expr.name == "msb")
|
||||||
|
return isClobberRisk(expr.args[0])
|
||||||
|
if (expr.name == "mkword")
|
||||||
|
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
|
||||||
|
return !expr.isSimple
|
||||||
|
}
|
||||||
|
else -> return !expr.isSimple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.size>1 && args.any { isClobberRisk(it) }
|
||||||
|
}
|
146
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
146
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import com.github.michaelbull.result.mapError
|
||||||
|
import prog8.code.core.*
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.isRegularFile
|
||||||
|
|
||||||
|
|
||||||
|
internal class AssemblyProgram(
|
||||||
|
override val name: String,
|
||||||
|
outputDir: Path,
|
||||||
|
private val compTarget: ICompilationTarget) : IAssemblyProgram {
|
||||||
|
|
||||||
|
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||||
|
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||||
|
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||||
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
|
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
|
||||||
|
private val listFile = outputDir.resolve("$name.list")
|
||||||
|
|
||||||
|
override fun assemble(options: CompilationOptions): Boolean {
|
||||||
|
|
||||||
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
|
when (compTarget.name) {
|
||||||
|
in setOf("c64", "c128", "cx16") -> {
|
||||||
|
// CBM machines .prg generation.
|
||||||
|
|
||||||
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||||
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
|
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
val outFile = when (options.output) {
|
||||||
|
OutputType.PRG -> {
|
||||||
|
command.add("--cbm-prg")
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
|
prgFile
|
||||||
|
}
|
||||||
|
OutputType.RAW -> {
|
||||||
|
command.add("--nostart")
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
|
binFile
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid output type")
|
||||||
|
}
|
||||||
|
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
|
||||||
|
}
|
||||||
|
"atari" -> {
|
||||||
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
|
// TODO are these options okay?
|
||||||
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
|
"--no-monitor"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
val outFile = when (options.output) {
|
||||||
|
OutputType.XEX -> {
|
||||||
|
command.add("--atari-xex")
|
||||||
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
|
xexFile
|
||||||
|
}
|
||||||
|
OutputType.RAW -> {
|
||||||
|
command.add("--nostart")
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
|
binFile
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid output type")
|
||||||
|
}
|
||||||
|
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid compilation target")
|
||||||
|
}
|
||||||
|
|
||||||
|
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||||
|
val result = proc.waitFor()
|
||||||
|
if (result == 0 && compTarget.name!="atari") {
|
||||||
|
removeGeneratedLabelsFromMonlist()
|
||||||
|
generateBreakpointList()
|
||||||
|
}
|
||||||
|
return result==0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
|
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
||||||
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
|
for (line in lines) {
|
||||||
|
if(pattern.matchEntire(line)==null)
|
||||||
|
it.write(line+"\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateBreakpointList() {
|
||||||
|
// builds list of breakpoints, appends to monitor list file
|
||||||
|
val breakpoints = mutableListOf<String>()
|
||||||
|
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
||||||
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
|
val match = pattern.matchEntire(line)
|
||||||
|
if (match != null)
|
||||||
|
breakpoints.add("break \$" + match.groupValues[1])
|
||||||
|
}
|
||||||
|
val num = breakpoints.size
|
||||||
|
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
||||||
|
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||||
|
breakpoints.add(2, "del")
|
||||||
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||||
|
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
||||||
|
return com.github.michaelbull.result.runCatching {
|
||||||
|
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
|
||||||
|
}.mapError { NoSuchFileException(File(filename)) }
|
||||||
|
} else {
|
||||||
|
val sib = Path(source.origin).resolveSibling(filename)
|
||||||
|
if (sib.isRegularFile())
|
||||||
|
Ok(SourceCode.File(sib).text)
|
||||||
|
else
|
||||||
|
Ok(SourceCode.File(Path(filename)).text)
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,73 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
|
||||||
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.DirectMemoryWrite
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.BuiltinFunctions
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.*
|
import prog8.compiler.FSignature
|
||||||
import prog8.compilerinterface.CpuType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import prog8.compilerinterface.FSignature
|
|
||||||
import prog8.compilerinterface.subroutineFloatEvalResultVar2
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
|
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
|
private val assignAsmGen: AssignmentAsmGen,
|
||||||
|
private val allocations: VariableAllocator) {
|
||||||
|
|
||||||
|
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) {
|
internal fun translateFunctioncallStatement(fcall: BuiltinFunctionCallStatement) {
|
||||||
|
val func = BuiltinFunctions.getValue(fcall.name)
|
||||||
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
||||||
|
val name = bfc.target.nameInSource.single()
|
||||||
|
val func = BuiltinFunctions.getValue(name)
|
||||||
|
val argExpression =
|
||||||
|
when(singleArg.kind) {
|
||||||
|
SourceStorageKind.LITERALNUMBER -> singleArg.number!!
|
||||||
|
SourceStorageKind.EXPRESSION -> singleArg.expression!!
|
||||||
|
SourceStorageKind.ARRAY -> singleArg.array!!
|
||||||
|
else -> {
|
||||||
|
// TODO make it so that we can assign efficiently from something else as an expression....namely: register(s)
|
||||||
|
// this is useful in pipe expressions for instance, to skip the use of a temporary variable
|
||||||
|
// but for now, just assign it to a temporary variable and use that as a source
|
||||||
|
// Idea: to do this without having to rewrite every single function in translateFunctioncall(),
|
||||||
|
// hack a special IdentifierReference like "!6502.A/X/Y/AX/AY/XY" to reference a cpu register
|
||||||
|
val tempvar = asmgen.getTempVarName(singleArg.datatype)
|
||||||
|
val assignTempvar = AsmAssignment(
|
||||||
|
singleArg,
|
||||||
|
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, singleArg.datatype, scope, variableAsmName = asmgen.asmVariableName(tempvar)),
|
||||||
|
false, program.memsizer, Position.DUMMY
|
||||||
|
)
|
||||||
|
assignAsmGen.translateNormalAssignment(assignTempvar)
|
||||||
|
// now use an expression to assign this tempvar
|
||||||
|
val ident = IdentifierReference(tempvar, Position.DUMMY)
|
||||||
|
ident.linkParents(scope)
|
||||||
|
ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val argExpressions = mutableListOf(argExpression)
|
||||||
|
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
||||||
|
fcall.linkParents(scope)
|
||||||
|
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
||||||
|
return if(isStatement) {
|
||||||
|
DataType.UNDEFINED
|
||||||
|
} else {
|
||||||
|
builtinFunctionReturnType(func.name).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if (discardResult && func.pure)
|
if (discardResult && func.pure)
|
||||||
return // can just ignore the whole function call altogether
|
return // can just ignore the whole function call altogether
|
||||||
@ -42,16 +83,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"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)
|
"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",
|
|
||||||
"cos8", "cos8u", "cos16", "cos16u" -> 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)
|
"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)
|
||||||
@ -64,7 +97,26 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
|
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
|
||||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||||
"pokew" -> funcPokeW(fcall)
|
"pokew" -> funcPokeW(fcall)
|
||||||
|
"pokemon" -> { /* meme function */ }
|
||||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||||
|
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
|
||||||
|
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
|
||||||
|
"pop" -> {
|
||||||
|
require(fcall.args[0] is IdentifierReference) {
|
||||||
|
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}"
|
||||||
|
}
|
||||||
|
asmgen.popCpuStack(DataType.UBYTE, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine)
|
||||||
|
}
|
||||||
|
"popw" -> {
|
||||||
|
require(fcall.args[0] is IdentifierReference) {
|
||||||
|
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}"
|
||||||
|
}
|
||||||
|
asmgen.popCpuStack(DataType.UWORD, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine)
|
||||||
|
}
|
||||||
|
"rsave" -> funcRsave()
|
||||||
|
"rsavex" -> funcRsaveX()
|
||||||
|
"rrestore" -> funcRrestore()
|
||||||
|
"rrestorex" -> funcRrestoreX()
|
||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall)
|
"callfar" -> funcCallFar(fcall)
|
||||||
"callrom" -> funcCallRom(fcall)
|
"callrom" -> funcCallRom(fcall)
|
||||||
@ -72,8 +124,59 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun funcRsave() {
|
||||||
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out("""
|
||||||
|
php
|
||||||
|
pha
|
||||||
|
phy
|
||||||
|
phx""")
|
||||||
|
else
|
||||||
|
// see http://6502.org/tutorials/register_preservation.html
|
||||||
|
asmgen.out("""
|
||||||
|
php
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
pha
|
||||||
|
lda P8ZP_SCRATCH_REG""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRsaveX() {
|
||||||
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" phx")
|
||||||
|
else
|
||||||
|
asmgen.out(" txa | pha")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRrestore() {
|
||||||
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out("""
|
||||||
|
plx
|
||||||
|
ply
|
||||||
|
pla
|
||||||
|
plp""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
plp""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRrestoreX() {
|
||||||
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" plx")
|
||||||
|
else
|
||||||
|
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
|
||||||
private fun funcCallFar(fcall: IFunctionCall) {
|
private fun funcCallFar(fcall: IFunctionCall) {
|
||||||
if(asmgen.options.compTarget !is Cx16Target)
|
if(asmgen.options.compTarget.name != "cx16")
|
||||||
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()
|
||||||
@ -87,7 +190,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
||||||
|
|
||||||
val argAddrArg = fcall.args[2]
|
val argAddrArg = fcall.args[2]
|
||||||
if(argAddrArg.constValue(program)?.number == 0) {
|
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
.word ${address.toHex()}
|
||||||
@ -104,7 +207,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${argAddrArg.number.toHex()}
|
lda ${argAddrArg.number.toHex()}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
@ -118,7 +221,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCallRom(fcall: IFunctionCall) {
|
private fun funcCallRom(fcall: IFunctionCall) {
|
||||||
if(asmgen.options.compTarget !is Cx16Target)
|
if(asmgen.options.compTarget.name != "cx16")
|
||||||
throw AssemblyError("callrom only works on cx16 target at this time")
|
throw AssemblyError("callrom 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()
|
||||||
@ -132,7 +235,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
throw AssemblyError("callrom bank must be <32")
|
throw AssemblyError("callrom bank must be <32")
|
||||||
|
|
||||||
val argAddrArg = fcall.args[2]
|
val argAddrArg = fcall.args[2]
|
||||||
if(argAddrArg.constValue(program)?.number == 0) {
|
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $01
|
lda $01
|
||||||
pha
|
pha
|
||||||
@ -157,7 +260,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
pla
|
pla
|
||||||
sta $01""")
|
sta $01""")
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $01
|
lda $01
|
||||||
pha
|
pha
|
||||||
@ -177,8 +280,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcCmp(fcall: IFunctionCall) {
|
private fun funcCmp(fcall: IFunctionCall) {
|
||||||
val arg1 = fcall.args[0]
|
val arg1 = fcall.args[0]
|
||||||
val arg2 = fcall.args[1]
|
val arg2 = fcall.args[1]
|
||||||
val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED)
|
val dt1 = arg1.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED)
|
val dt2 = arg2.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
if(dt1 in ByteDatatypes) {
|
if(dt1 in ByteDatatypes) {
|
||||||
if(dt2 in ByteDatatypes) {
|
if(dt2 in ByteDatatypes) {
|
||||||
when (arg2) {
|
when (arg2) {
|
||||||
@ -186,12 +289,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp #${arg2.number}")
|
asmgen.out(" cmp #${arg2.number.toInt()}")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if(arg2.addressExpression is NumericLiteralValue) {
|
if(arg2.addressExpression is NumericLiteral) {
|
||||||
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 {
|
||||||
@ -220,12 +323,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
cmp ${asmgen.asmVariableName(arg2)}
|
cmp ${asmgen.asmVariableName(arg2)}
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy #>${arg2.number}
|
cpy #>${arg2.number.toInt()}
|
||||||
bne +
|
bne +
|
||||||
cmp #<${arg2.number}
|
cmp #<${arg2.number.toInt()}
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -244,15 +347,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if(discardResult || fcall !is FunctionCall)
|
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 nameRef = fcall.args[0] as IdentifierReference
|
val name = (fcall.args[0] as StringLiteral).value
|
||||||
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
|
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||||
val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
|
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
|
||||||
|
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
|
||||||
|
|
||||||
val existingSize = asmgen.slabs[name]
|
val existing = allocations.getMemorySlab(name)
|
||||||
if(existingSize!=null && existingSize!=size)
|
if(existing!=null && (existing.first!=size || existing.second!=align))
|
||||||
throw AssemblyError("memory slab '$name' already exists with a different size ($size) at ${fcall.position}")
|
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)
|
val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position)
|
||||||
slabname.linkParents(fcall)
|
slabname.linkParents(fcall)
|
||||||
@ -264,7 +368,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, 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)
|
||||||
asmgen.slabs[name] = size
|
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?) {
|
||||||
@ -277,23 +381,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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", "cos8", "cos8u" -> {
|
|
||||||
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", "cos16", "cos16u" -> {
|
|
||||||
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) {
|
||||||
@ -376,8 +463,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteral) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteral).number
|
||||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||||
@ -419,14 +506,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteral) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteral).number
|
||||||
asmgen.out(" ror ${number.toHex()}")
|
asmgen.out(" ror ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -477,8 +564,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteral) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteral).number
|
||||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||||
@ -520,14 +607,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteral) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteral).number
|
||||||
asmgen.out(" rol ${number.toHex()}")
|
asmgen.out(" rol ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -572,16 +659,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
|
||||||
translateArguments(fcall.args, func, scope)
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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?) {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall.args, func, scope)
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
@ -619,92 +696,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
} 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, program, 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) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
val first = fcall.args[0]
|
val first = fcall.args[0]
|
||||||
@ -750,8 +750,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
// optimized simple case: swap two memory locations
|
// optimized simple case: swap two memory locations
|
||||||
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||||
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
val addr1 = (first.addressExpression as? NumericLiteral)?.number?.toHex()
|
||||||
val addr2 = (second.addressExpression as? NumericLiteralValue)?.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 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
|
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
||||||
|
|
||||||
@ -782,10 +782,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if(pointerVariable != null
|
if(pointerVariable != null
|
||||||
&& pointerVariable isSameAs secondExpr.left
|
&& pointerVariable isSameAs secondExpr.left
|
||||||
&& firstExpr.operator == "+" && secondExpr.operator == "+"
|
&& firstExpr.operator == "+" && secondExpr.operator == "+"
|
||||||
&& (firstOffset is NumericLiteralValue || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
&& (firstOffset is NumericLiteral || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
||||||
&& (secondOffset is NumericLiteralValue || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
&& (secondOffset is NumericLiteral || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
||||||
) {
|
) {
|
||||||
if(firstOffset is NumericLiteralValue && secondOffset is NumericLiteralValue) {
|
if(firstOffset is NumericLiteral && secondOffset is NumericLiteral) {
|
||||||
if(firstOffset!=secondOffset) {
|
if(firstOffset!=secondOffset) {
|
||||||
swapArrayValues(
|
swapArrayValues(
|
||||||
DataType.UBYTE,
|
DataType.UBYTE,
|
||||||
@ -822,13 +822,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
|
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
|
||||||
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
|
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
|
||||||
val elementIDt = first.inferType(program)
|
val elementIDt = first.inferType(program)
|
||||||
if(!elementIDt.isKnown)
|
val elementDt = elementIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
|
||||||
|
|
||||||
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
|
val firstNum = first.indexer.indexExpr as? NumericLiteral
|
||||||
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
||||||
val secondNum = second.indexer.indexExpr as? NumericLiteralValue
|
val secondNum = second.indexer.indexExpr as? NumericLiteral
|
||||||
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
||||||
|
|
||||||
if(firstNum!=null && secondNum!=null) {
|
if(firstNum!=null && secondNum!=null) {
|
||||||
@ -897,7 +895,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexValue2: NumericLiteral) {
|
||||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -1011,7 +1009,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -1069,7 +1067,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteral) {
|
||||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -1132,27 +1130,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
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, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1180,7 +1172,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcPokeW(fcall: IFunctionCall) {
|
private fun funcPokeW(fcall: IFunctionCall) {
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
asmgen.out(" sta $addr | sty ${addr}+1")
|
asmgen.out(" sta $addr | sty ${addr}+1")
|
||||||
@ -1211,13 +1203,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
val index = (addrExpr.right as NumericLiteral).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
ldy #$index
|
||||||
sta ($varname),y
|
sta ($varname),y
|
||||||
@ -1229,6 +1221,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> throw AssemblyError("wrong pokew arg type")
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
|
||||||
@ -1238,7 +1231,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
asmgen.out(" lda $addr | ldy ${addr}+1")
|
asmgen.out(" lda $addr | ldy ${addr}+1")
|
||||||
}
|
}
|
||||||
@ -1268,11 +1261,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
val index = (addrExpr.right as NumericLiteral).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
ldy #$index
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
@ -1319,14 +1312,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
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 NumericLiteralValue || fcall.args[0] is IdentifierReference)
|
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteral || fcall.args[0] is IdentifierReference)
|
||||||
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 NumericLiteralValue && mr0.addressExpression !is IdentifierReference
|
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
|
||||||
if (mr1 != null)
|
if (mr1 != null)
|
||||||
needAsave = needAsave or (mr1.addressExpression !is NumericLiteralValue && mr1.addressExpression !is IdentifierReference)
|
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference)
|
||||||
}
|
}
|
||||||
when(reg) {
|
when(reg) {
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
@ -1369,7 +1362,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (!arg.inferType(program).isWords)
|
if (!arg.inferType(program).isWords)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteral)
|
||||||
throw AssemblyError("msb(const) should have been const-folded away")
|
throw AssemblyError("msb(const) should have been const-folded away")
|
||||||
if (arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmVariableName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
@ -1380,6 +1373,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||||
|
in Cx16VirtualRegisters -> {
|
||||||
|
val regname = resultRegister.name.lowercase()
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("invalid reg")
|
else -> throw AssemblyError("invalid reg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1413,7 +1416,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (!arg.inferType(program).isWords)
|
if (!arg.inferType(program).isWords)
|
||||||
throw AssemblyError("lsb required word argument")
|
throw AssemblyError("lsb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteral)
|
||||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||||
|
|
||||||
if (arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
@ -1425,6 +1428,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||||
|
in Cx16VirtualRegisters -> {
|
||||||
|
val regname = resultRegister.name.lowercase()
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("invalid reg")
|
else -> throw AssemblyError("invalid reg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1463,8 +1476,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
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
|
||||||
@ -1475,7 +1491,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
||||||
val callConv = signature.callConvention(args.map { it.inferType(program).getOr(DataType.UNDEFINED) })
|
val callConv = signature.callConvention(args.map {
|
||||||
|
it.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
})
|
||||||
|
|
||||||
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
||||||
return when (value) {
|
return when (value) {
|
||||||
@ -1483,14 +1501,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val addr = AddressOf(value, value.position)
|
val addr = AddressOf(value, value.position)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
throw AssemblyError("float literals should have been converted into autovar")
|
throw AssemblyError("float literals should have been converted into autovar")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(scope==null)
|
if(scope==null)
|
||||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||||
|
|
||||||
scope.asmGenInfo.usedFloatEvalResultVar2 = true
|
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||||
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
|
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
|
||||||
val addr = AddressOf(variable, value.position)
|
val addr = AddressOf(variable, value.position)
|
||||||
addr.linkParents(value)
|
addr.linkParents(value)
|
862
codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt
Normal file
862
codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt
Normal file
@ -0,0 +1,862 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
internal class ExpressionsAsmGen(private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
|
private val allocator: VariableAllocator) {
|
||||||
|
|
||||||
|
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||||
|
internal fun translateExpression(expression:Expression) {
|
||||||
|
if (this.asmgen.options.slowCodegenWarnings) {
|
||||||
|
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
|
||||||
|
}
|
||||||
|
translateExpressionInternal(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// the rest of the methods are all PRIVATE
|
||||||
|
|
||||||
|
|
||||||
|
private fun translateExpressionInternal(expression: Expression) {
|
||||||
|
|
||||||
|
when(expression) {
|
||||||
|
is PrefixExpression -> translateExpression(expression)
|
||||||
|
is BinaryExpression -> translateExpression(expression)
|
||||||
|
is ArrayIndexedExpression -> translateExpression(expression)
|
||||||
|
is TypecastExpression -> translateExpression(expression)
|
||||||
|
is AddressOf -> translateExpression(expression)
|
||||||
|
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
||||||
|
is NumericLiteral -> translateExpression(expression)
|
||||||
|
is IdentifierReference -> translateExpression(expression)
|
||||||
|
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
||||||
|
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 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 CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
||||||
|
else -> TODO("missing expression asmgen for $expression")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) {
|
||||||
|
// only for use in nested expression evaluation
|
||||||
|
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
asmgen.saveXbeforeCall(call)
|
||||||
|
asmgen.translateFunctionCall(call, true)
|
||||||
|
if(sub.regXasResult()) {
|
||||||
|
// store the return value in X somewhere that we can access again below
|
||||||
|
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||||
|
}
|
||||||
|
asmgen.restoreXafterCall(call)
|
||||||
|
|
||||||
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
|
for ((_, reg) in returns) {
|
||||||
|
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
||||||
|
if (reg.registerOrPair != null) {
|
||||||
|
when (reg.registerOrPair!!) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
|
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||||
|
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
|
||||||
|
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
|
||||||
|
RegisterOrPair.R0,
|
||||||
|
RegisterOrPair.R1,
|
||||||
|
RegisterOrPair.R2,
|
||||||
|
RegisterOrPair.R3,
|
||||||
|
RegisterOrPair.R4,
|
||||||
|
RegisterOrPair.R5,
|
||||||
|
RegisterOrPair.R6,
|
||||||
|
RegisterOrPair.R7,
|
||||||
|
RegisterOrPair.R8,
|
||||||
|
RegisterOrPair.R9,
|
||||||
|
RegisterOrPair.R10,
|
||||||
|
RegisterOrPair.R11,
|
||||||
|
RegisterOrPair.R12,
|
||||||
|
RegisterOrPair.R13,
|
||||||
|
RegisterOrPair.R14,
|
||||||
|
RegisterOrPair.R15 -> {
|
||||||
|
asmgen.out(
|
||||||
|
"""
|
||||||
|
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else when(reg.statusflag) {
|
||||||
|
Statusflag.Pc -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
rol a
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
Statusflag.Pz -> {
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
Statusflag.Pv -> {
|
||||||
|
asmgen.out("""
|
||||||
|
bvs +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
Statusflag.Pn -> {
|
||||||
|
asmgen.out("""
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
null -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(typecast: TypecastExpression) {
|
||||||
|
translateExpressionInternal(typecast.expression)
|
||||||
|
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when(typecast.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
when(typecast.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when(typecast.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
when(typecast.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when(typecast.type) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
|
||||||
|
DataType.FLOAT -> {}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
|
||||||
|
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
||||||
|
}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: AddressOf) {
|
||||||
|
val name = asmgen.asmVariableName(expr.identifier)
|
||||||
|
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: NumericLiteral) {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
|
lda #<${expr.number.toHex()}
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda #>${expr.number.toHex()}
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex
|
||||||
|
""")
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val floatConst = allocator.getFloatAsmConst(expr.number)
|
||||||
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
|
val varname = asmgen.asmVariableName(expr)
|
||||||
|
when(expr.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||||
|
}
|
||||||
|
in IterableDatatypes -> {
|
||||||
|
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
|
// Uses evalstack to evaluate the given expression.
|
||||||
|
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
|
||||||
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
val rightIDt = expr.right.inferType(program)
|
||||||
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
|
throw AssemblyError("can't infer type of both expression operands")
|
||||||
|
|
||||||
|
val leftDt = leftIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
val rightDt = rightIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
// see if we can apply some optimized routines
|
||||||
|
when(expr.operator) {
|
||||||
|
"+" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
|
translateExpressionInternal(expr.right)
|
||||||
|
if(rightDt in ByteDatatypes) {
|
||||||
|
val incdec = if(leftVal<0) "dec" else "inc"
|
||||||
|
repeat(leftVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(leftVal<0) {
|
||||||
|
repeat(leftVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(leftVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if (rightVal!=null && rightVal in -4..4)
|
||||||
|
{
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
val incdec = if(rightVal<0) "dec" else "inc"
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(rightVal<0) {
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(rightVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"-" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
|
{
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
val incdec = if(rightVal<0) "inc" else "dec"
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(rightVal>0) {
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(rightVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
">>" -> {
|
||||||
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(amount!=null) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
when (leftDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if (amount <= 2)
|
||||||
|
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||||
|
else {
|
||||||
|
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||||
|
repeat(amount) { asmgen.out(" lsr a") }
|
||||||
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if (amount <= 2)
|
||||||
|
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||||
|
else {
|
||||||
|
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||||
|
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||||
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount>=16) {
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var left = amount
|
||||||
|
while (left >= 7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_uw_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount>=16) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_HI+1,x
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
sta P8ESTACK_HI+1,x
|
||||||
|
beq ++
|
||||||
|
+ lda #255
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
sta P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var left = amount
|
||||||
|
while (left >= 7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_w_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_right_w_$left")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"<<" -> {
|
||||||
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(amount!=null) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
if (leftDt in ByteDatatypes) {
|
||||||
|
if (amount <= 2)
|
||||||
|
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||||
|
else {
|
||||||
|
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||||
|
repeat(amount) { asmgen.out(" asl a") }
|
||||||
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var left = amount
|
||||||
|
while (left >= 7) {
|
||||||
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_left_w_$left")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"*" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val leftVar = expr.left as? IdentifierReference
|
||||||
|
val rightVar = expr.right as? IdentifierReference
|
||||||
|
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
||||||
|
return translateSquared(leftVar, leftDt)
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = expr.right.constValue(program)
|
||||||
|
if(value!=null) {
|
||||||
|
if(rightDt in IntegerDatatypes) {
|
||||||
|
val amount = value.number.toInt()
|
||||||
|
if(amount==2) {
|
||||||
|
// optimize x*2 common case
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when(rightDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(rightVal!=null && rightVal==2) {
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
when(leftDt) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
|
DataType.BYTE -> asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror 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")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in ComparisonOperators -> {
|
||||||
|
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(rightVal==0)
|
||||||
|
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||||
|
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||||
|
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||||
|
|
||||||
|
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
|
||||||
|
translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// the general, non-optimized cases TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||||
|
translateExpressionInternal(expr.left)
|
||||||
|
translateExpressionInternal(expr.right)
|
||||||
|
when (leftDt) {
|
||||||
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
|
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||||
|
else -> throw AssemblyError("non-numerical datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
|
||||||
|
translateExpressionInternal(expr)
|
||||||
|
when(operator) {
|
||||||
|
"==" -> {
|
||||||
|
when(dt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
when(dt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"<" -> {
|
||||||
|
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||||
|
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position))
|
||||||
|
when(dt) {
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
">" -> {
|
||||||
|
when(dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"<=" -> {
|
||||||
|
when(dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzeros_b")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
">=" -> {
|
||||||
|
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||||
|
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position))
|
||||||
|
when(dt) {
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid comparison operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateSquared(variable: IdentifierReference, dt: DataType) {
|
||||||
|
val asmVar = asmgen.asmVariableName(variable)
|
||||||
|
when(dt) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
|
asmgen.out(" lda $asmVar")
|
||||||
|
asmgen.signExtendAYlsb(dt)
|
||||||
|
asmgen.out(" jsr math.square")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("require integer dt for square")
|
||||||
|
}
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: PrefixExpression) {
|
||||||
|
translateExpressionInternal(expr.expression)
|
||||||
|
val itype = expr.inferType(program)
|
||||||
|
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
when(expr.operator) {
|
||||||
|
"+" -> {}
|
||||||
|
"-" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"~" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes ->
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
eor #255
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
""")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||||
|
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}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
||||||
|
val elementIDt = arrayExpr.inferType(program)
|
||||||
|
if(!elementIDt.isKnown)
|
||||||
|
throw AssemblyError("unknown dt")
|
||||||
|
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
||||||
|
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||||
|
val constIndexNum = arrayExpr.indexer.constIndex()
|
||||||
|
if(constIndexNum!=null) {
|
||||||
|
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird element type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>$arrayVarName
|
||||||
|
clc
|
||||||
|
adc #<$arrayVarName
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr floats.push_float""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||||
|
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||||
|
"%" -> {
|
||||||
|
if(types==DataType.BYTE)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+2,x
|
||||||
|
clc
|
||||||
|
adc P8ESTACK_LO+1,x
|
||||||
|
inx
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
""")
|
||||||
|
"-" -> asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+2,x
|
||||||
|
sec
|
||||||
|
sbc P8ESTACK_LO+1,x
|
||||||
|
inx
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
""")
|
||||||
|
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
|
||||||
|
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
|
||||||
|
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||||
|
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||||
|
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||||
|
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||||
|
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||||
|
"%" -> {
|
||||||
|
if(dt==DataType.WORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||||
|
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||||
|
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||||
|
">>" -> {
|
||||||
|
if(dt==DataType.UWORD)
|
||||||
|
asmgen.out(" jsr math.shift_right_uw")
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_right_w")
|
||||||
|
}
|
||||||
|
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||||
|
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||||
|
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||||
|
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorFloats(operator: String) {
|
||||||
|
when(operator) {
|
||||||
|
"*" -> asmgen.out(" jsr floats.mul_f")
|
||||||
|
"/" -> asmgen.out(" jsr floats.div_f")
|
||||||
|
"+" -> asmgen.out(" jsr floats.add_f")
|
||||||
|
"-" -> asmgen.out(" jsr floats.sub_f")
|
||||||
|
"<" -> asmgen.out(" jsr floats.less_f")
|
||||||
|
">" -> asmgen.out(" jsr floats.greater_f")
|
||||||
|
"<=" -> asmgen.out(" jsr floats.lesseq_f")
|
||||||
|
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||||
|
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||||
|
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||||
|
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) {
|
||||||
|
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
||||||
|
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
||||||
|
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||||
|
when(operator) {
|
||||||
|
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||||
|
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||||
|
"<=" -> asmgen.out("""
|
||||||
|
bpl +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta P8ESTACK_LO,x""")
|
||||||
|
">=" -> asmgen.out("""
|
||||||
|
bmi +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta P8ESTACK_LO,x""")
|
||||||
|
"<" -> asmgen.out("""
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO,x""")
|
||||||
|
">" -> asmgen.out("""
|
||||||
|
bpl +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO,x""")
|
||||||
|
}
|
||||||
|
asmgen.out(" dex")
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,36 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ArrayToElementTypes
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.RegisterOrPair
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpression
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.toHex
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
|
||||||
import prog8.compilerinterface.toConstantIntegerRange
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) {
|
||||||
|
|
||||||
internal fun translate(stmt: ForLoop) {
|
internal fun translate(stmt: ForLoop) {
|
||||||
val iterableDt = stmt.iterable.inferType(program)
|
val iterableDt = stmt.iterable.inferType(program)
|
||||||
if(!iterableDt.isKnown)
|
if(!iterableDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
when(stmt.iterable) {
|
when(stmt.iterable) {
|
||||||
is RangeExpr -> {
|
is RangeExpression -> {
|
||||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
val range = (stmt.iterable as RangeExpression).toConstantIntegerRange()
|
||||||
if(range==null) {
|
if(range==null) {
|
||||||
translateForOverNonconstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
translateForOverNonconstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as RangeExpression)
|
||||||
} else {
|
} else {
|
||||||
translateForOverConstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), range)
|
translateForOverConstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as IdentifierReference)
|
translateForOverIterableVar(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as IdentifierReference)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
@ -43,7 +39,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||||
|
|
||||||
if(stepsize < -1) {
|
if(stepsize < -1) {
|
||||||
val limit = range.to.constValue(program)?.number?.toDouble()
|
val limit = range.to.constValue(program)?.number
|
||||||
if(limit==0.0)
|
if(limit==0.0)
|
||||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||||
}
|
}
|
||||||
@ -289,13 +285,15 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
if(length>=16) {
|
||||||
// allocate index var on ZP
|
// allocate index var on ZP if possible
|
||||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
result.fold(
|
||||||
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("$indexVar .byte 0")
|
||||||
$indexVar .byte 0""")
|
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
@ -328,13 +326,15 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
if(length>=16) {
|
||||||
// allocate index var on ZP
|
// allocate index var on ZP if possible
|
||||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
result.fold(
|
||||||
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("$indexVar .byte 0")
|
||||||
$indexVar .byte 0""")
|
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
@ -588,6 +588,10 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
private fun assignLoopvar(stmt: ForLoop, range: RangeExpression) =
|
||||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).getOr(DataType.UNDEFINED), stmt.definingSubroutine)
|
asmgen.assignExpressionToVariable(
|
||||||
|
range.from,
|
||||||
|
asmgen.asmVariableName(stmt.loopVar),
|
||||||
|
stmt.loopVarDt(program).getOrElse { throw AssemblyError("unknown dt") },
|
||||||
|
stmt.definingSubroutine)
|
||||||
}
|
}
|
274
codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
Normal file
274
codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.AddressOf
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||||
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
|
import prog8.codegen.cpu6502.assignment.AsmAssignment
|
||||||
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
|
|
||||||
|
|
||||||
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) {
|
||||||
|
saveXbeforeCall(stmt)
|
||||||
|
translateFunctionCall(stmt, false)
|
||||||
|
restoreXafterCall(stmt)
|
||||||
|
// just ignore any result values from the function call.
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun saveXbeforeCall(stmt: IFunctionCall) {
|
||||||
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
|
if(sub.shouldSaveX()) {
|
||||||
|
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, (stmt as Node).definingSubroutine!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
|
if(sub.shouldSaveX()) {
|
||||||
|
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 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) =
|
||||||
|
(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)
|
||||||
|
|
||||||
|
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
|
||||||
|
// 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 code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||||
|
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||||
|
|
||||||
|
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${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) {
|
||||||
|
argumentsViaRegisters(sub, call)
|
||||||
|
if (sub.inline && asmgen.options.optimize) {
|
||||||
|
// inline the subroutine.
|
||||||
|
// we do this by copying the subroutine's statements at the call site.
|
||||||
|
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||||
|
// (this condition has been enforced by an ast check earlier)
|
||||||
|
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||||
|
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
|
||||||
|
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" jsr $subAsmName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(sub.inline)
|
||||||
|
throw AssemblyError("can only reliably inline asmsub routines at this time")
|
||||||
|
|
||||||
|
if(optimizeIntArgsViaRegisters(sub)) {
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||||
|
} else {
|
||||||
|
// 2 byte params, second in Y, first in A
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
||||||
|
if(!call.args[1].isSimple)
|
||||||
|
asmgen.out(" pha")
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
||||||
|
if(!call.args[1].isSimple)
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// arguments via variables
|
||||||
|
for(arg in sub.parameters.withIndex().zip(call.args))
|
||||||
|
argumentViaVariable(sub, arg.first.value, arg.second)
|
||||||
|
}
|
||||||
|
asmgen.out(" jsr $subAsmName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) {
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
|
||||||
|
} else {
|
||||||
|
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) {
|
||||||
|
registerArgsViaCpuStackEvaluation(call, sub)
|
||||||
|
} else {
|
||||||
|
asmsub6502ArgsEvalOrder(sub).forEach {
|
||||||
|
val param = sub.parameters[it]
|
||||||
|
val arg = call.args[it]
|
||||||
|
argumentViaRegister(sub, IndexedValue(it, param), arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerArgsViaCpuStackEvaluation(call: IFunctionCall, callee: Subroutine) {
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
require(callee.isAsmSubroutine)
|
||||||
|
if(callee.parameters.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
// use the cpu hardware stack as intermediate storage for the arguments.
|
||||||
|
val argOrder = asmsub6502ArgsEvalOrder(callee)
|
||||||
|
argOrder.reversed().forEach {
|
||||||
|
asmgen.pushCpuStack(callee.parameters[it].type, call.args[it])
|
||||||
|
}
|
||||||
|
argOrder.forEach {
|
||||||
|
val param = callee.parameters[it]
|
||||||
|
val targetVar = callee.searchAsmParameter(param.name)!!
|
||||||
|
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) {
|
||||||
|
// pass parameter via a regular variable (not via registers)
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
if(!isArgumentTypeCompatible(valueDt, parameter.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
|
val varName = asmgen.asmVariableName(sub.scopedName + parameter.name)
|
||||||
|
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) {
|
||||||
|
// pass argument via a register parameter
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
|
val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null)
|
||||||
|
val statusflag = paramRegister.statusflag
|
||||||
|
val register = paramRegister.registerOrPair
|
||||||
|
val requiredDt = parameter.value.type
|
||||||
|
if(requiredDt!=valueDt) {
|
||||||
|
if(valueDt largerThan requiredDt)
|
||||||
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
|
}
|
||||||
|
if (statusflag!=null) {
|
||||||
|
if(requiredDt!=valueDt)
|
||||||
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
|
if (statusflag == Statusflag.Pc) {
|
||||||
|
// this param needs to be set last, right before the jsr
|
||||||
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
|
when(value) {
|
||||||
|
is NumericLiteral -> {
|
||||||
|
val carrySet = value.number.toInt() != 0
|
||||||
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmVariableName(value)
|
||||||
|
asmgen.out("""
|
||||||
|
pha
|
||||||
|
clc
|
||||||
|
lda $sourceName
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
+ pla""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// via register or register pair
|
||||||
|
register!!
|
||||||
|
if(requiredDt largerThan valueDt) {
|
||||||
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
||||||
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
|
||||||
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
||||||
|
} else {
|
||||||
|
val target: AsmAssignTarget =
|
||||||
|
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)
|
||||||
|
else {
|
||||||
|
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
||||||
|
AsmAssignTarget.fromRegisters(register, signed, sub, program, asmgen)
|
||||||
|
}
|
||||||
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
|
if(value is IdentifierReference) {
|
||||||
|
val addr = AddressOf(value, Position.DUMMY)
|
||||||
|
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
}
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
|
if(argType isAssignableTo paramType)
|
||||||
|
return true
|
||||||
|
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||||
|
return true
|
||||||
|
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||||
|
return true
|
||||||
|
|
||||||
|
// we have a special rule for some types.
|
||||||
|
// strings are assignable to UWORD, for example, and vice versa
|
||||||
|
if(argType==DataType.STR && paramType==DataType.UWORD)
|
||||||
|
return true
|
||||||
|
if(argType==DataType.UWORD && paramType == DataType.STR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.ast.toHex
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
|
||||||
|
|
||||||
|
|
||||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -41,7 +39,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
targetMemory!=null -> {
|
targetMemory!=null -> {
|
||||||
when (val addressExpr = targetMemory.addressExpression) {
|
when (val addressExpr = targetMemory.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val what = addressExpr.number.toHex()
|
val what = addressExpr.number.toHex()
|
||||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
}
|
}
|
||||||
@ -83,7 +81,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
||||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
656
codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
Normal file
656
codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
Normal file
@ -0,0 +1,656 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.code.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
|
import prog8.compiler.CallGraph
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the main parts of the program:
|
||||||
|
* - entry/exit code
|
||||||
|
* - initialization routines
|
||||||
|
* - blocks
|
||||||
|
* - subroutines
|
||||||
|
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
|
||||||
|
*/
|
||||||
|
internal class ProgramAndVarsGen(
|
||||||
|
val program: Program,
|
||||||
|
val options: CompilationOptions,
|
||||||
|
val errors: IErrorReporter,
|
||||||
|
private val symboltable: SymbolTable,
|
||||||
|
private val functioncallAsmGen: FunctionCallAsmGen,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
|
private val allocator: VariableAllocator,
|
||||||
|
private val zeropage: Zeropage
|
||||||
|
) {
|
||||||
|
private val compTarget = options.compTarget
|
||||||
|
private val callGraph = CallGraph(program, true)
|
||||||
|
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
|
||||||
|
|
||||||
|
internal fun generate() {
|
||||||
|
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
|
||||||
|
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
|
||||||
|
|
||||||
|
header()
|
||||||
|
val allBlocks = program.allBlocks
|
||||||
|
if(allBlocks.first().name != "main")
|
||||||
|
throw AssemblyError("first block should be 'main'")
|
||||||
|
|
||||||
|
if(errors.noErrors()) {
|
||||||
|
program.allBlocks.forEach { block2asm(it) }
|
||||||
|
memorySlabs()
|
||||||
|
footer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun header() {
|
||||||
|
val ourName = this.javaClass.name
|
||||||
|
val cpu = when(compTarget.machine.cpu) {
|
||||||
|
CpuType.CPU6502 -> "6502"
|
||||||
|
CpuType.CPU65c02 -> "w65c02"
|
||||||
|
else -> "unsupported"
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("; $cpu assembly code for '${program.name}'")
|
||||||
|
asmgen.out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
|
||||||
|
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
|
||||||
|
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
||||||
|
asmgen.out("")
|
||||||
|
asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
|
||||||
|
|
||||||
|
// the global prog8 variables needed
|
||||||
|
val zp = zeropage
|
||||||
|
asmgen.out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
|
||||||
|
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
|
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
|
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||||
|
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||||
|
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||||
|
|
||||||
|
when(options.output) {
|
||||||
|
OutputType.RAW -> {
|
||||||
|
asmgen.out("; ---- raw assembler program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||||
|
}
|
||||||
|
OutputType.PRG -> {
|
||||||
|
when(options.launcher) {
|
||||||
|
CbmPrgLauncherType.BASIC -> {
|
||||||
|
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
||||||
|
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.toplevelModule.position)
|
||||||
|
}
|
||||||
|
asmgen.out("; ---- basic program with sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
val year = LocalDate.now().year
|
||||||
|
asmgen.out(" .word (+), $year")
|
||||||
|
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||||
|
asmgen.out("+\t.word 0")
|
||||||
|
asmgen.out("prog8_entrypoint\t; assembly code starts here\n")
|
||||||
|
if(!options.noSysInit)
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
|
}
|
||||||
|
CbmPrgLauncherType.NONE -> {
|
||||||
|
asmgen.out("; ---- program without basic sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||||
|
if(!options.noSysInit)
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutputType.XEX -> {
|
||||||
|
asmgen.out("; ---- atari xex program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||||
|
if(!options.noSysInit)
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
||||||
|
asmgen.out("""
|
||||||
|
; zeropage is clobbered so we need to reset the machine at exit
|
||||||
|
lda #>sys.reset_system
|
||||||
|
pha
|
||||||
|
lda #<sys.reset_system
|
||||||
|
pha""")
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
|
||||||
|
when(compTarget.name) {
|
||||||
|
"cx16" -> {
|
||||||
|
if(options.floats)
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
else -> asmgen.jmp("main.start")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun memorySlabs() {
|
||||||
|
asmgen.out("; memory slabs")
|
||||||
|
asmgen.out("prog8_slabs\t.block")
|
||||||
|
for((name, info) in allocator.memorySlabs) {
|
||||||
|
if(info.second>1u)
|
||||||
|
asmgen.out("\t.align ${info.second.toHex()}")
|
||||||
|
asmgen.out("$name\t.fill ${info.first}")
|
||||||
|
}
|
||||||
|
asmgen.out("\t.bend")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun block2asm(block: Block) {
|
||||||
|
asmgen.out("")
|
||||||
|
asmgen.out("; ---- block: '${block.name}' ----")
|
||||||
|
if(block.address!=null)
|
||||||
|
asmgen.out("* = ${block.address!!.toHex()}")
|
||||||
|
else {
|
||||||
|
if("align_word" in block.options())
|
||||||
|
asmgen.out("\t.align 2")
|
||||||
|
else if("align_page" in block.options())
|
||||||
|
asmgen.out("\t.align $100")
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||||
|
asmgen.outputSourceLine(block)
|
||||||
|
|
||||||
|
createBlockVariables(block)
|
||||||
|
asmsubs2asm(block.statements)
|
||||||
|
|
||||||
|
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 statements = block.statements.filterNot { it in initializers }
|
||||||
|
val (subroutine, stmts) = statements.partition { it is Subroutine }
|
||||||
|
stmts.forEach { asmgen.translate(it) }
|
||||||
|
subroutine.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
|
if(!options.dontReinitGlobals) {
|
||||||
|
// generate subroutine to initialize block-level (global) variables
|
||||||
|
if (initializers.isNotEmpty()) {
|
||||||
|
asmgen.out("prog8_init_vars\t.proc\n")
|
||||||
|
initializers.forEach { assign -> asmgen.translate(assign) }
|
||||||
|
asmgen.out(" rts\n .pend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getVars(scope: StNode): Map<String, StNode> =
|
||||||
|
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
||||||
|
|
||||||
|
private fun createBlockVariables(block: Block) {
|
||||||
|
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
|
||||||
|
require(scope.type==StNodeType.BLOCK)
|
||||||
|
val varsInBlock = getVars(scope)
|
||||||
|
|
||||||
|
// Zeropage Variables
|
||||||
|
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||||
|
zeropagevars2asm(varnames)
|
||||||
|
|
||||||
|
// MemDefs and Consts
|
||||||
|
val mvs = varsInBlock
|
||||||
|
.filter { it.value.type==StNodeType.MEMVAR }
|
||||||
|
.map { it.value as StMemVar }
|
||||||
|
val consts = varsInBlock
|
||||||
|
.filter { it.value.type==StNodeType.CONSTANT }
|
||||||
|
.map { it.value as StConstant }
|
||||||
|
memdefsAndConsts2asm(mvs, consts)
|
||||||
|
|
||||||
|
// normal statically allocated variables
|
||||||
|
val variables = varsInBlock
|
||||||
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||||
|
.map { it.value as StStaticVariable }
|
||||||
|
nonZpVariables2asm(variables)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translateSubroutine(sub: Subroutine) {
|
||||||
|
var onlyVariables = false
|
||||||
|
|
||||||
|
if(sub.inline) {
|
||||||
|
if(options.optimize) {
|
||||||
|
if(sub.isAsmSubroutine || callGraph.unused(sub))
|
||||||
|
return
|
||||||
|
|
||||||
|
// from an inlined subroutine only the local variables are generated,
|
||||||
|
// all other code statements are omitted in the subroutine itself
|
||||||
|
// (they've been inlined at the call site, remember?)
|
||||||
|
onlyVariables = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("")
|
||||||
|
|
||||||
|
if(sub.isAsmSubroutine) {
|
||||||
|
if(sub.asmAddress!=null)
|
||||||
|
return // already done at the memvars section
|
||||||
|
|
||||||
|
// asmsub with most likely just an inline asm in it
|
||||||
|
asmgen.out("${sub.name}\t.proc")
|
||||||
|
sub.statements.forEach { asmgen.translate(it) }
|
||||||
|
asmgen.out(" .pend\n")
|
||||||
|
} else {
|
||||||
|
// regular subroutine
|
||||||
|
asmgen.out("${sub.name}\t.proc")
|
||||||
|
|
||||||
|
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||||
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
|
val varsInSubroutine = getVars(scope)
|
||||||
|
|
||||||
|
// Zeropage Variables
|
||||||
|
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||||
|
zeropagevars2asm(varnames)
|
||||||
|
|
||||||
|
// MemDefs and Consts
|
||||||
|
val mvs = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.MEMVAR }
|
||||||
|
.map { it.value as StMemVar }
|
||||||
|
val consts = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.CONSTANT }
|
||||||
|
.map { it.value as StConstant }
|
||||||
|
memdefsAndConsts2asm(mvs, consts)
|
||||||
|
|
||||||
|
asmsubs2asm(sub.statements)
|
||||||
|
|
||||||
|
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||||
|
if(sub.name=="start" && sub.definingBlock.name=="main")
|
||||||
|
entrypointInitialization()
|
||||||
|
|
||||||
|
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||||
|
asmgen.out("; simple int arg(s) passed via register(s)")
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
val dt = sub.parameters[0].type
|
||||||
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
||||||
|
if(dt in ByteDatatypes)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||||
|
else
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||||
|
} else {
|
||||||
|
require(sub.parameters.size==2)
|
||||||
|
// 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 target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!onlyVariables) {
|
||||||
|
asmgen.out("; statements")
|
||||||
|
sub.statements.forEach { asmgen.translate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("; variables")
|
||||||
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
|
if(addr!=null)
|
||||||
|
asmgen.out("$name = $addr")
|
||||||
|
else when(dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out("$name .byte 0")
|
||||||
|
DataType.UWORD -> asmgen.out("$name .word 0")
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||||
|
asmgen.out("prog8_regsaveA .byte 0")
|
||||||
|
if(asmGenInfo.usedRegsaveX)
|
||||||
|
asmgen.out("prog8_regsaveX .byte 0")
|
||||||
|
if(asmGenInfo.usedRegsaveY)
|
||||||
|
asmgen.out("prog8_regsaveY .byte 0")
|
||||||
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
|
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
||||||
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
|
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||||
|
|
||||||
|
// normal statically allocated variables
|
||||||
|
val variables = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||||
|
.map { it.value as StStaticVariable }
|
||||||
|
nonZpVariables2asm(variables)
|
||||||
|
|
||||||
|
asmgen.out(" .pend\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun entrypointInitialization() {
|
||||||
|
asmgen.out("; program startup initialization")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
if(!options.dontReinitGlobals) {
|
||||||
|
blockVariableInitializers.forEach {
|
||||||
|
if (it.value.isNotEmpty())
|
||||||
|
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// string and array variables in zeropage that have initializer value, should be initialized
|
||||||
|
val stringVarsWithInitInZp = getZpStringVarsWithInitvalue()
|
||||||
|
val arrayVarsWithInitInZp = getZpArrayVarsWithInitvalue()
|
||||||
|
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
|
||||||
|
asmgen.out("; zp str and array initializations")
|
||||||
|
stringVarsWithInitInZp.forEach {
|
||||||
|
val name = asmgen.asmVariableName(it.name)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${name}
|
||||||
|
ldy #>${name}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<${name}_init_value
|
||||||
|
ldy #>${name}_init_value
|
||||||
|
jsr prog8_lib.strcpy""")
|
||||||
|
}
|
||||||
|
arrayVarsWithInitInZp.forEach {
|
||||||
|
val size = it.alloc.size
|
||||||
|
val name = asmgen.asmVariableName(it.name)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${name}_init_value
|
||||||
|
ldy #>${name}_init_value
|
||||||
|
sta cx16.r0L
|
||||||
|
sty cx16.r0H
|
||||||
|
lda #<${name}
|
||||||
|
ldy #>${name}
|
||||||
|
sta cx16.r1L
|
||||||
|
sty cx16.r1H
|
||||||
|
lda #<$size
|
||||||
|
ldy #>$size
|
||||||
|
jsr sys.memcopy""")
|
||||||
|
}
|
||||||
|
asmgen.out(" jmp +")
|
||||||
|
}
|
||||||
|
|
||||||
|
stringVarsWithInitInZp.forEach {
|
||||||
|
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||||
|
outputStringvar(varname, it.value.second, it.value.first)
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayVarsWithInitInZp.forEach {
|
||||||
|
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||||
|
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("""+ tsx
|
||||||
|
stx prog8_lib.orig_stackpointer ; required for sys.exit()
|
||||||
|
ldx #255 ; init estack ptr
|
||||||
|
clv
|
||||||
|
clc""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ZpStringWithInitial(
|
||||||
|
val name: List<String>,
|
||||||
|
val alloc: Zeropage.ZpAllocation,
|
||||||
|
val value: Pair<String, Encoding>
|
||||||
|
)
|
||||||
|
|
||||||
|
private class ZpArrayWithInitial(
|
||||||
|
val name: List<String>,
|
||||||
|
val alloc: Zeropage.ZpAllocation,
|
||||||
|
val value: StArray
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
|
||||||
|
val result = mutableListOf<ZpStringWithInitial>()
|
||||||
|
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
||||||
|
for (variable in vars) {
|
||||||
|
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
|
||||||
|
if(svar.initialStringValue!=null)
|
||||||
|
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
|
||||||
|
val result = mutableListOf<ZpArrayWithInitial>()
|
||||||
|
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
||||||
|
for (variable in vars) {
|
||||||
|
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
|
||||||
|
if(svar.initialArrayValue!=null)
|
||||||
|
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun zeropagevars2asm(varNames: Set<List<String>>) {
|
||||||
|
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }
|
||||||
|
for ((scopedName, zpvar) in zpVariables) {
|
||||||
|
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
|
||||||
|
asmgen.out("${scopedName.last()} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
||||||
|
asmgen.out("")
|
||||||
|
asmgen.out("; non-zeropage variables")
|
||||||
|
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
|
||||||
|
stringvars.forEach {
|
||||||
|
outputStringvar(it.name, it.initialStringValue!!.second, it.initialStringValue!!.first)
|
||||||
|
}
|
||||||
|
othervars.sortedBy { it.type }.forEach {
|
||||||
|
staticVariable2asm(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||||
|
val name = variable.name
|
||||||
|
val initialValue: Number =
|
||||||
|
if(variable.initialNumericValue!=null) {
|
||||||
|
if(variable.dt== DataType.FLOAT)
|
||||||
|
variable.initialNumericValue!!
|
||||||
|
else
|
||||||
|
variable.initialNumericValue!!.toInt()
|
||||||
|
} else 0
|
||||||
|
|
||||||
|
when (variable.dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out("$name\t.byte ${initialValue.toHex()}")
|
||||||
|
DataType.BYTE -> asmgen.out("$name\t.char $initialValue")
|
||||||
|
DataType.UWORD -> asmgen.out("$name\t.word ${initialValue.toHex()}")
|
||||||
|
DataType.WORD -> asmgen.out("$name\t.sint $initialValue")
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
if(initialValue==0) {
|
||||||
|
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
|
||||||
|
} else {
|
||||||
|
val floatFill = compTarget.machine.getFloat(initialValue).makeFloatFillAsm()
|
||||||
|
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
throw AssemblyError("all string vars should have been interned into prog")
|
||||||
|
}
|
||||||
|
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.length)
|
||||||
|
else -> {
|
||||||
|
throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
|
||||||
|
when(dt) {
|
||||||
|
DataType.ARRAY_UB -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
|
if (data.size <= 16)
|
||||||
|
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
asmgen.out(varname)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
asmgen.out(" .byte " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B -> {
|
||||||
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
|
if (data.size <= 16)
|
||||||
|
asmgen.out("$varname\t.char ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
asmgen.out(varname)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
asmgen.out(" .char " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
|
if (data.size <= 16)
|
||||||
|
asmgen.out("$varname\t.word ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
asmgen.out(varname)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
asmgen.out(" .word " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> {
|
||||||
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
|
if (data.size <= 16)
|
||||||
|
asmgen.out("$varname\t.sint ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
asmgen.out(varname)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
asmgen.out(" .sint " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
|
val floatFills = array.map {
|
||||||
|
compTarget.machine.getFloat(it.number!!).makeFloatFillAsm()
|
||||||
|
}
|
||||||
|
asmgen.out(varname)
|
||||||
|
for (f in array.zip(floatFills))
|
||||||
|
asmgen.out(" .byte ${f.second} ; float ${f.first}")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("require array dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun zeroFilledArray(numElts: Int): StArray {
|
||||||
|
val values = mutableListOf<StArrayElement>()
|
||||||
|
repeat(numElts) {
|
||||||
|
values.add(StArrayElement(0.0, null))
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
|
||||||
|
memvars.forEach {
|
||||||
|
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
||||||
|
}
|
||||||
|
consts.forEach {
|
||||||
|
if(it.dt==DataType.FLOAT)
|
||||||
|
asmgen.out(" ${it.name} = ${it.value}")
|
||||||
|
else
|
||||||
|
asmgen.out(" ${it.name} = ${it.value.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asmsubs2asm(statements: List<Statement>) {
|
||||||
|
statements
|
||||||
|
.filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null }
|
||||||
|
.forEach { asmsub ->
|
||||||
|
asmsub as Subroutine
|
||||||
|
asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
||||||
|
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"")
|
||||||
|
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||||
|
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
|
for (chunk in outputBytes.chunked(16))
|
||||||
|
asmgen.out(" .byte " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||||
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
|
return when (dt) {
|
||||||
|
DataType.ARRAY_UB ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = it.number!!.toInt()
|
||||||
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> array.map {
|
||||||
|
if(it.number!=null) {
|
||||||
|
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
else if(it.addressOf!=null) {
|
||||||
|
asmgen.asmSymbolName(it.addressOf!!)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw AssemblyError("weird array elt")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||||
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
|
return when (dt) {
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
DataType.ARRAY_UB ->
|
||||||
|
array.map {
|
||||||
|
val number = it.number!!.toInt()
|
||||||
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B ->
|
||||||
|
array.map {
|
||||||
|
val number = it.number!!.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> array.map {
|
||||||
|
val number = it.number!!.toInt()
|
||||||
|
"$" + number.toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> array.map {
|
||||||
|
val number = it.number!!.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
137
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal file
137
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
|
import com.github.michaelbull.result.onSuccess
|
||||||
|
import prog8.code.StNode
|
||||||
|
import prog8.code.StNodeType
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||||
|
private val options: CompilationOptions,
|
||||||
|
private val errors: IErrorReporter
|
||||||
|
) {
|
||||||
|
|
||||||
|
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 zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables
|
||||||
|
|
||||||
|
init {
|
||||||
|
allocateZeropageVariables()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getMemorySlab(name: String) = memorySlabsInternal[name]
|
||||||
|
|
||||||
|
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 {
|
||||||
|
val asmName = globalFloatConsts[number]
|
||||||
|
if(asmName!=null)
|
||||||
|
return asmName
|
||||||
|
|
||||||
|
val newName = "prog8_float_const_${globalFloatConsts.size}"
|
||||||
|
globalFloatConsts[number] = newName
|
||||||
|
return newName
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate variables into the Zeropage.
|
||||||
|
* The result should be retrieved from the current machine's zeropage object!
|
||||||
|
*/
|
||||||
|
private fun allocateZeropageVariables() {
|
||||||
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
|
return
|
||||||
|
|
||||||
|
val allVariables = collectAllVariables(symboltable)
|
||||||
|
|
||||||
|
val numberOfAllocatableVariables = allVariables.size
|
||||||
|
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||||
|
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||||
|
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
|
||||||
|
val numberOfExplicitNonZpVariables = allVariables.count { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||||
|
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
|
||||||
|
|
||||||
|
var numVariablesAllocatedInZP = 0
|
||||||
|
var numberOfNonIntegerVariables = 0
|
||||||
|
|
||||||
|
varsRequiringZp.forEach { variable ->
|
||||||
|
val result = zeropage.allocate(
|
||||||
|
variable.scopedName,
|
||||||
|
variable.dt,
|
||||||
|
variable.length,
|
||||||
|
variable.position,
|
||||||
|
errors
|
||||||
|
)
|
||||||
|
result.fold(
|
||||||
|
success = {
|
||||||
|
numVariablesAllocatedInZP++
|
||||||
|
},
|
||||||
|
failure = {
|
||||||
|
errors.err(it.message!!, variable.position)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errors.noErrors()) {
|
||||||
|
varsPreferringZp.forEach { variable ->
|
||||||
|
val result = zeropage.allocate(
|
||||||
|
variable.scopedName,
|
||||||
|
variable.dt,
|
||||||
|
variable.length,
|
||||||
|
variable.position,
|
||||||
|
errors
|
||||||
|
)
|
||||||
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
|
// no need to check for allocation error, if there is one, just allocate in normal system ram.
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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? ...?
|
||||||
|
if(errors.noErrors()) {
|
||||||
|
for (variable in varsDontCare.sortedBy { it.scopedName.size }) {
|
||||||
|
if(variable.dt in IntegerDatatypes) {
|
||||||
|
if(zeropage.free.isEmpty()) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
val result = zeropage.allocate(
|
||||||
|
variable.scopedName,
|
||||||
|
variable.dt,
|
||||||
|
variable.length,
|
||||||
|
variable.position,
|
||||||
|
errors
|
||||||
|
)
|
||||||
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
numberOfNonIntegerVariables++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||||
|
println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||||
|
println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||||
|
val vars = mutableListOf<StStaticVariable>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type == StNodeType.STATICVAR)
|
||||||
|
vars.add(child.value as StStaticVariable)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(st)
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compilerinterface.IMemSizer
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
@ -39,8 +37,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
val origAstTarget: AssignTarget? = null
|
val origAstTarget: AssignTarget? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
||||||
val asmVarname: String by lazy {
|
val asmVarname: String by lazy {
|
||||||
if (array == null)
|
if (array == null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
@ -56,16 +54,29 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget {
|
||||||
val idt = inferType(program)
|
with(assign.target) {
|
||||||
if(!idt.isKnown)
|
val idt = inferType(program)
|
||||||
throw AssemblyError("unknown dt")
|
val dt = idt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
val dt = idt.getOr(DataType.UNDEFINED)
|
when {
|
||||||
when {
|
identifier != null -> {
|
||||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter
|
||||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
|
if (parameter!=null) {
|
||||||
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
val sub = parameter.definingSubroutine!!
|
||||||
else -> throw AssemblyError("weird target")
|
if (sub.isAsmSubroutine) {
|
||||||
|
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
|
||||||
|
if(reg.statusflag!=null)
|
||||||
|
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||||
|
else
|
||||||
|
return AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AsmAssignTarget(TargetStorageKind.VARIABLE, program, 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)
|
||||||
|
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,12 +118,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val array: ArrayIndexedExpression? = null,
|
val array: ArrayIndexedExpression? = null,
|
||||||
val memory: DirectMemoryRead? = null,
|
val memory: DirectMemoryRead? = null,
|
||||||
val register: RegisterOrPair? = null,
|
val register: RegisterOrPair? = null,
|
||||||
val number: NumericLiteralValue? = null,
|
val number: NumericLiteral? = null,
|
||||||
val expression: Expression? = null
|
val expression: Expression? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
||||||
|
|
||||||
val asmVarname: String
|
val asmVarname: String
|
||||||
get() = if(array==null)
|
get() = if(array==null)
|
||||||
@ -129,10 +140,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
|
|
||||||
return when(value) {
|
return when(value) {
|
||||||
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv)
|
is NumericLiteral -> throw AssemblyError("should have been constant value")
|
||||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
is ArrayLiteral -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
|
val parameter = value.targetVarDecl(program)?.subroutineParameter
|
||||||
|
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine)
|
||||||
|
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
||||||
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
val varName=asmgen.asmVariableName(value)
|
val varName=asmgen.asmVariableName(value)
|
||||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||||
@ -148,33 +162,23 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||||
}
|
}
|
||||||
is FunctionCall -> {
|
is BuiltinFunctionCall -> {
|
||||||
when (val sub = value.target.targetStatement(program)) {
|
val returnType = value.inferType(program)
|
||||||
is Subroutine -> {
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
|
||||||
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
|
}
|
||||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
is FunctionCallExpression -> {
|
||||||
|
val sub = value.target.targetSubroutine(program)!!
|
||||||
|
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
|
||||||
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val returnType = value.inferType(program)
|
|
||||||
if(!returnType.isKnown)
|
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOr(DataType.UNDEFINED), expression = value)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw AssemblyError("weird call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val dt = value.inferType(program)
|
val returnType = value.inferType(program)
|
||||||
if(!dt.isKnown)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.getOr(DataType.UNDEFINED), expression = value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +213,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
|
|||||||
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" }
|
||||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||||
"source storage size must be less or equal to target datatype storage size"
|
"source dt size must be less or equal to target dt size at $position"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,23 +1,18 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.toHex
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import prog8.compilerinterface.BuiltinFunctions
|
|
||||||
import prog8.compilerinterface.CpuType
|
|
||||||
import prog8.compilerinterface.builtinFunctionReturnType
|
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
|
internal class AssignmentAsmGen(private val program: Program,
|
||||||
private val exprAsmgen: ExpressionsAsmGen
|
private val asmgen: AsmGen,
|
||||||
) {
|
private val allocator: VariableAllocator) {
|
||||||
|
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
|
|
||||||
|
|
||||||
fun translate(assignment: Assignment) {
|
fun translate(assignment: Assignment) {
|
||||||
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
||||||
@ -32,6 +27,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun virtualRegsToVariables(origtarget: AsmAssignTarget): AsmAssignTarget {
|
||||||
|
return if(origtarget.kind==TargetStorageKind.REGISTER && origtarget.register in Cx16VirtualRegisters) {
|
||||||
|
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, origtarget.datatype, origtarget.scope,
|
||||||
|
variableAsmName = "cx16.${origtarget.register!!.name.lowercase()}", origAstTarget = origtarget.origAstTarget)
|
||||||
|
} else origtarget
|
||||||
|
}
|
||||||
|
|
||||||
fun translateNormalAssignment(assign: AsmAssignment) {
|
fun translateNormalAssignment(assign: AsmAssignment) {
|
||||||
if(assign.isAugmentable) {
|
if(assign.isAugmentable) {
|
||||||
augmentableAsmGen.translate(assign)
|
augmentableAsmGen.translate(assign)
|
||||||
@ -43,9 +45,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
// simple case: assign a constant number
|
// simple case: assign a constant number
|
||||||
val num = assign.source.number!!.number
|
val num = assign.source.number!!.number
|
||||||
when (assign.target.datatype) {
|
when (assign.target.datatype) {
|
||||||
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toShort())
|
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toInt())
|
||||||
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
||||||
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
DataType.FLOAT -> assignConstantFloat(assign.target, num)
|
||||||
else -> throw AssemblyError("weird numval type")
|
else -> throw AssemblyError("weird numval type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +86,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue")
|
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
|
||||||
assignFloatFromAY(assign.target)
|
assignFloatFromAY(assign.target)
|
||||||
}
|
}
|
||||||
else ->
|
else ->
|
||||||
@ -121,17 +123,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
SourceStorageKind.MEMORY -> {
|
SourceStorageKind.MEMORY -> {
|
||||||
fun assignViaExprEval(expression: Expression) {
|
fun assignViaExprEval(expression: Expression) {
|
||||||
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope)
|
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope)
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
|
||||||
else
|
|
||||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
|
||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
val value = assign.source.memory!!
|
val value = assign.source.memory!!
|
||||||
when (value.addressExpression) {
|
when (value.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
val address = (value.addressExpression as NumericLiteral).number.toUInt()
|
||||||
assignMemoryByte(assign.target, address, null)
|
assignMemoryByte(assign.target, address, null)
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
@ -148,139 +147,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
when(val value = assign.source.expression!!) {
|
assignExpression(assign)
|
||||||
is AddressOf -> {
|
|
||||||
val sourceName = asmgen.asmSymbolName(value.identifier)
|
|
||||||
assignAddressOf(assign.target, sourceName)
|
|
||||||
}
|
|
||||||
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
|
||||||
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
|
||||||
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
|
||||||
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
|
||||||
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
|
|
||||||
is FunctionCall -> {
|
|
||||||
when (val sub = value.target.targetStatement(program)) {
|
|
||||||
is Subroutine -> {
|
|
||||||
asmgen.saveXbeforeCall(value)
|
|
||||||
asmgen.translateFunctionCall(value)
|
|
||||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
|
|
||||||
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
|
|
||||||
when (returnValue.first) {
|
|
||||||
DataType.STR -> {
|
|
||||||
asmgen.restoreXafterCall(value)
|
|
||||||
when(assign.target.datatype) {
|
|
||||||
DataType.UWORD -> {
|
|
||||||
// assign the address of the string result value
|
|
||||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
|
||||||
// copy the actual string result into the target string variable
|
|
||||||
asmgen.out("""
|
|
||||||
pha
|
|
||||||
lda #<${assign.target.asmVarname}
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda #>${assign.target.asmVarname}
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
pla
|
|
||||||
jsr prog8_lib.strcpy""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird target dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
// float result from function sits in FAC1
|
|
||||||
asmgen.restoreXafterCall(value)
|
|
||||||
assignFAC1float(assign.target)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// do NOT restore X register before assigning the result values first
|
|
||||||
when (returnValue.second.registerOrPair) {
|
|
||||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
|
||||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
|
||||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
|
||||||
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
|
||||||
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
|
||||||
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
|
||||||
else -> {
|
|
||||||
val sflag = returnValue.second.statusflag
|
|
||||||
if(sflag!=null)
|
|
||||||
assignStatusFlagByte(assign.target, sflag)
|
|
||||||
else
|
|
||||||
throw AssemblyError("should be just one register byte result value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we've processed the result value in the X register by now, so it's now finally safe to restore it
|
|
||||||
asmgen.restoreXafterCall(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val signature = BuiltinFunctions.getValue(sub.name)
|
|
||||||
asmgen.translateBuiltinFunctionCallExpression(value, signature, false, assign.target.register)
|
|
||||||
if(assign.target.register==null) {
|
|
||||||
// still need to assign the result to the target variable/etc.
|
|
||||||
val returntype = builtinFunctionReturnType(sub.name, value.args, program)
|
|
||||||
if(!returntype.isKnown)
|
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
when(returntype.getOr(DataType.UNDEFINED)) {
|
|
||||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
|
||||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
|
||||||
DataType.STR -> {
|
|
||||||
when (assign.target.datatype) {
|
|
||||||
DataType.STR -> {
|
|
||||||
asmgen.out("""
|
|
||||||
pha
|
|
||||||
lda #<${assign.target.asmVarname}
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda #>${assign.target.asmVarname}
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
pla
|
|
||||||
jsr prog8_lib.strcpy""")
|
|
||||||
}
|
|
||||||
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
|
||||||
else -> throw AssemblyError("str return value type mismatch with target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
// float result from function sits in FAC1
|
|
||||||
assignFAC1float(assign.target)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird result type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw AssemblyError("weird func call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PrefixExpression -> {
|
|
||||||
// first assign the value to the target then apply the operator in place on the target.
|
|
||||||
translateNormalAssignment(AsmAssignment(
|
|
||||||
AsmAssignSource.fromAstSource(value.expression, program, asmgen),
|
|
||||||
assign.target,
|
|
||||||
false, program.memsizer, assign.position
|
|
||||||
))
|
|
||||||
when(value.operator) {
|
|
||||||
"+" -> {}
|
|
||||||
"-" -> augmentableAsmGen.inplaceNegate(assign.target, assign.target.datatype)
|
|
||||||
"~" -> augmentableAsmGen.inplaceInvert(assign.target, assign.target.datatype)
|
|
||||||
"not" -> augmentableAsmGen.inplaceBooleanNot(assign.target, assign.target.datatype)
|
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// Everything else just evaluate via the stack.
|
|
||||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
|
||||||
// because the code here is the implementation of exactly that...)
|
|
||||||
// TODO FIX THIS... by using a temp var? so that it becomes augmentable assignment expression?
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
|
||||||
asmgen.signExtendStackLsb(assign.source.datatype)
|
|
||||||
if(assign.target.kind!=TargetStorageKind.STACK || assign.target.datatype != assign.source.datatype)
|
|
||||||
assignStackValue(assign.target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SourceStorageKind.REGISTER -> {
|
SourceStorageKind.REGISTER -> {
|
||||||
asmgen.assignRegister(assign.source.register!!, assign.target)
|
asmgen.assignRegister(assign.source.register!!, assign.target)
|
||||||
@ -292,6 +159,258 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignExpression(assign: AsmAssignment) {
|
||||||
|
when(val value = assign.source.expression!!) {
|
||||||
|
is AddressOf -> {
|
||||||
|
val sourceName = asmgen.asmSymbolName(value.identifier)
|
||||||
|
assignAddressOf(assign.target, sourceName)
|
||||||
|
}
|
||||||
|
is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber")
|
||||||
|
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
||||||
|
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
||||||
|
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
||||||
|
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
|
||||||
|
is FunctionCallExpression -> {
|
||||||
|
val sub = value.target.targetSubroutine(program)!!
|
||||||
|
asmgen.saveXbeforeCall(value)
|
||||||
|
asmgen.translateFunctionCall(value, true)
|
||||||
|
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
|
||||||
|
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
|
||||||
|
when (returnValue.first) {
|
||||||
|
DataType.STR -> {
|
||||||
|
asmgen.restoreXafterCall(value)
|
||||||
|
when(assign.target.datatype) {
|
||||||
|
DataType.UWORD -> {
|
||||||
|
// assign the address of the string result value
|
||||||
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
throw AssemblyError("stringvalue assignment should have been replaced by a call to strcpy")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
// float result from function sits in FAC1
|
||||||
|
asmgen.restoreXafterCall(value)
|
||||||
|
assignFAC1float(assign.target)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// do NOT restore X register before assigning the result values first
|
||||||
|
when (returnValue.second.registerOrPair) {
|
||||||
|
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||||
|
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||||
|
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||||
|
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
||||||
|
else -> {
|
||||||
|
val sflag = returnValue.second.statusflag
|
||||||
|
if(sflag!=null)
|
||||||
|
assignStatusFlagByte(assign.target, sflag)
|
||||||
|
else
|
||||||
|
throw AssemblyError("should be just one register byte result value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we've processed the result value in the X register by now, so it's now finally safe to restore it
|
||||||
|
asmgen.restoreXafterCall(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BuiltinFunctionCall -> {
|
||||||
|
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
|
||||||
|
if(assign.target.register==null) {
|
||||||
|
// still need to assign the result to the target variable/etc.
|
||||||
|
val returntype = builtinFunctionReturnType(value.name)
|
||||||
|
if(!returntype.isKnown)
|
||||||
|
throw AssemblyError("unknown dt")
|
||||||
|
when(returntype.getOr(DataType.UNDEFINED)) {
|
||||||
|
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||||
|
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||||
|
DataType.STR -> {
|
||||||
|
when (assign.target.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
asmgen.out("""
|
||||||
|
pha
|
||||||
|
lda #<${assign.target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>${assign.target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
pla
|
||||||
|
jsr prog8_lib.strcpy""")
|
||||||
|
}
|
||||||
|
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
|
else -> throw AssemblyError("str return value type mismatch with target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
// float result from function sits in FAC1
|
||||||
|
assignFAC1float(assign.target)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird result type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PrefixExpression -> {
|
||||||
|
// first assign the value to the target then apply the operator in place on the target.
|
||||||
|
translateNormalAssignment(AsmAssignment(
|
||||||
|
AsmAssignSource.fromAstSource(value.expression, program, asmgen),
|
||||||
|
assign.target,
|
||||||
|
false, program.memsizer, assign.position
|
||||||
|
))
|
||||||
|
val target = virtualRegsToVariables(assign.target)
|
||||||
|
when(value.operator) {
|
||||||
|
"+" -> {}
|
||||||
|
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
|
||||||
|
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
|
||||||
|
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
|
||||||
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ContainmentCheck -> {
|
||||||
|
containmentCheckIntoA(value)
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
is PipeExpression -> {
|
||||||
|
asmgen.translatePipeExpression(value.source, value.segments, value, false, false)
|
||||||
|
val resultDt = value.inferType(program)
|
||||||
|
val register =
|
||||||
|
if(resultDt.isBytes) RegisterOrPair.A
|
||||||
|
else if(resultDt.isWords) RegisterOrPair.AY
|
||||||
|
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
||||||
|
else throw AssemblyError("invalid dt")
|
||||||
|
asmgen.assignRegister(register, assign.target)
|
||||||
|
}
|
||||||
|
is BinaryExpression -> {
|
||||||
|
if(value.operator in ComparisonOperators) {
|
||||||
|
// TODO real optimized code for comparison expressions that yield a boolean result value
|
||||||
|
assignConstantByte(assign.target, 0)
|
||||||
|
val origTarget = assign.target.origAstTarget
|
||||||
|
if(origTarget!=null) {
|
||||||
|
val assignTrue = AnonymousScope(mutableListOf(
|
||||||
|
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||||
|
), assign.position)
|
||||||
|
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||||
|
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
|
||||||
|
ifelse.linkParents(value)
|
||||||
|
asmgen.translate(ifelse)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// no orig ast assign target so can't use the workaround, so fallback to stack eval
|
||||||
|
fallbackToStackEval(assign)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// All remaining binary expressions just evaluate via the stack for now.
|
||||||
|
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||||
|
// because the code here is the implementation of exactly that...)
|
||||||
|
fallbackToStackEval(assign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird assignment value type $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fallbackToStackEval(assign: AsmAssignment) {
|
||||||
|
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
|
||||||
|
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.
|
||||||
|
// this routine is called for assigning a binaryexpression value:
|
||||||
|
// - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node)
|
||||||
|
// - for all other binary expressions.
|
||||||
|
asmgen.translateExpression(assign.source.expression!!)
|
||||||
|
if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
||||||
|
asmgen.signExtendStackLsb(assign.source.datatype)
|
||||||
|
if (assign.target.kind != TargetStorageKind.STACK || assign.target.datatype != assign.source.datatype)
|
||||||
|
assignStackValue(assign.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containmentCheckIntoA(containment: ContainmentCheck) {
|
||||||
|
val elementDt = containment.element.inferType(program)
|
||||||
|
val variable = (containment.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||||
|
?: throw AssemblyError("invalid containment iterable type")
|
||||||
|
|
||||||
|
if(variable.origin!=VarDeclOrigin.USERCODE) {
|
||||||
|
when(variable.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
require(elementDt.isBytes)
|
||||||
|
val stringVal = variable.value as StringLiteral
|
||||||
|
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
// require(elementDt istype DataType.FLOAT)
|
||||||
|
throw AssemblyError("containment check of floats not supported")
|
||||||
|
}
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
require(elementDt.isInteger)
|
||||||
|
val arrayVal = variable.value as ArrayLiteral
|
||||||
|
val dt = elementDt.getOr(DataType.UNDEFINED)
|
||||||
|
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
||||||
|
when(dt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
||||||
|
when(variable.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
// use subroutine
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
|
val stringVal = variable.value as StringLiteral
|
||||||
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
throw AssemblyError("containment check of floats not supported")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
val arrayVal = variable.value as ArrayLiteral
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
val arrayVal = variable.value as ArrayLiteral
|
||||||
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
||||||
when(statusflag) {
|
when(statusflag) {
|
||||||
Statusflag.Pc -> {
|
Statusflag.Pc -> {
|
||||||
@ -312,9 +431,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) {
|
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) {
|
||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val valueDt = valueIDt.getOr(DataType.UNDEFINED)
|
|
||||||
if(valueDt==targetDt)
|
if(valueDt==targetDt)
|
||||||
throw AssemblyError("type cast to identical dt should have been removed")
|
throw AssemblyError("type cast to identical dt should have been removed")
|
||||||
|
|
||||||
@ -336,16 +453,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
fun assignViaExprEval(addressExpression: Expression) {
|
fun assignViaExprEval(addressExpression: Expression) {
|
||||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
|
||||||
else
|
|
||||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
|
||||||
assignRegisterByte(target, CpuRegister.A)
|
assignRegisterByte(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (value.addressExpression) {
|
when (value.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
val address = (value.addressExpression as NumericLiteral).number.toUInt()
|
||||||
assignMemoryByteIntoWord(target, address, null)
|
assignMemoryByteIntoWord(target, address, null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -367,7 +481,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> throw AssemblyError("a cast of a literal value should have been const-folded away")
|
is NumericLiteral -> throw AssemblyError("a cast of a literal value should have been const-folded away")
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,13 +559,51 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No more special optmized cases yet. Do the rest via more complex evaluation
|
if(target.kind==TargetStorageKind.REGISTER) {
|
||||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
if(valueDt==DataType.FLOAT && target.datatype!=DataType.FLOAT) {
|
||||||
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
// have to typecast the float number on the fly down to an integer
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
|
||||||
|
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
|
||||||
|
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes)
|
||||||
|
} else {
|
||||||
|
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(targetDt==DataType.FLOAT && (target.register==RegisterOrPair.FAC1 || target.register==RegisterOrPair.FAC2)) {
|
||||||
|
when(valueDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.Y, false)
|
||||||
|
asmgen.out(" jsr floats.FREADUY")
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.A, true)
|
||||||
|
asmgen.out(" jsr floats.FREADSA")
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.AY, false)
|
||||||
|
asmgen.out(" jsr floats.GIVUAYFAY")
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||||
|
asmgen.out(" jsr floats.GIVAYFAY")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
if(target.register==RegisterOrPair.FAC2) {
|
||||||
|
asmgen.out(" jsr floats.MOVEF")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No more special optmized cases yet. Do the rest via more complex evaluation
|
||||||
|
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||||
|
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
|
||||||
|
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {
|
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {
|
||||||
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
|
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
|
||||||
lsb.linkParents(value.parent)
|
lsb.linkParents(value.parent)
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||||
val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
|
||||||
@ -779,7 +931,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if(target.constArrayIndexValue!=null) {
|
if(target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
||||||
@ -795,8 +947,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
lda #<(${target.asmVarname}+$scaledIdx)
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
ldy #>(${target.asmVarname}+$scaledIdx)
|
||||||
jsr floats.pop_float
|
jsr floats.pop_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -989,7 +1141,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
target.array!!
|
target.array!!
|
||||||
if(target.constArrayIndexValue!=null) {
|
if(target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
@ -1008,8 +1160,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>$sourceName
|
ldy #>$sourceName
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
lda #<(${target.asmVarname}+$scaledIdx)
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
ldy #>(${target.asmVarname}+$scaledIdx)
|
||||||
jsr floats.copy_float
|
jsr floats.copy_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -1044,7 +1196,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
adc #<${target.asmVarname}
|
adc #<${target.asmVarname}
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr floats.copy_float""")
|
+ jsr floats.copy_float""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -1078,6 +1230,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun assignFAC2float(target: AsmAssignTarget) {
|
||||||
|
asmgen.out(" jsr floats.MOVFA") // fac2 -> fac1
|
||||||
|
assignFAC1float(target)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun assignFAC1float(target: AsmAssignTarget) {
|
internal fun assignFAC1float(target: AsmAssignTarget) {
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -1107,7 +1264,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
if (target.register!! != RegisterOrPair.FAC1)
|
if (target.register!! != RegisterOrPair.FAC1)
|
||||||
throw AssemblyError("can't assign Fac1 float to another fac register")
|
throw AssemblyError("can't assign Fac1 float to another register")
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_fac1")
|
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_fac1")
|
||||||
}
|
}
|
||||||
@ -1156,17 +1313,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $sourceName
|
lda #<$sourceName
|
||||||
sta ${target.asmVarname}
|
ldy #>$sourceName
|
||||||
lda $sourceName+1
|
sta P8ZP_SCRATCH_W1
|
||||||
sta ${target.asmVarname}+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda $sourceName+2
|
lda #<${target.asmVarname}
|
||||||
sta ${target.asmVarname}+2
|
ldy #>${target.asmVarname}
|
||||||
lda $sourceName+3
|
jsr floats.copy_float""")
|
||||||
sta ${target.asmVarname}+3
|
|
||||||
lda $sourceName+4
|
|
||||||
sta ${target.asmVarname}+4
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1213,7 +1366,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1266,7 +1419,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (wordtarget.constArrayIndexValue!=null) {
|
if (wordtarget.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
|
||||||
asmgen.out(" lda $sourceName")
|
asmgen.out(" lda $sourceName")
|
||||||
asmgen.signExtendAYlsb(DataType.BYTE)
|
asmgen.signExtendAYlsb(DataType.BYTE)
|
||||||
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
@ -1287,7 +1440,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
pha
|
pha
|
||||||
ora #$7f
|
ora #$7f
|
||||||
bmi +
|
bmi +
|
||||||
ldx #0
|
lda #0
|
||||||
+ tax
|
+ tax
|
||||||
pla""")
|
pla""")
|
||||||
RegisterOrPair.AY -> asmgen.out("""
|
RegisterOrPair.AY -> asmgen.out("""
|
||||||
@ -1295,7 +1448,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
pha
|
pha
|
||||||
ora #$7f
|
ora #$7f
|
||||||
bmi +
|
bmi +
|
||||||
ldy #0
|
lda #0
|
||||||
+ tay
|
+ tay
|
||||||
pla""")
|
pla""")
|
||||||
RegisterOrPair.XY -> asmgen.out("""
|
RegisterOrPair.XY -> asmgen.out("""
|
||||||
@ -1303,9 +1456,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
tax
|
tax
|
||||||
ora #$7f
|
ora #$7f
|
||||||
bmi +
|
bmi +
|
||||||
ldy #0
|
lda #0
|
||||||
+ tay""")
|
+ tay""")
|
||||||
else -> throw AssemblyError("only reg pairs are words")
|
in Cx16VirtualRegisters -> {
|
||||||
|
val regname = wordtarget.register.name.lowercase()
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta cx16.$regname
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta cx16.$regname+1""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("only reg pairs allowed as word target ${wordtarget.register}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
@ -1334,7 +1497,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (wordtarget.constArrayIndexValue!=null) {
|
if (wordtarget.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
|
||||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
@ -1356,7 +1519,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
|
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
|
||||||
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
|
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
||||||
else -> throw AssemblyError("only reg pairs are words")
|
in Cx16VirtualRegisters -> {
|
||||||
|
val regname = wordtarget.register.name.lowercase()
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("only reg pairs allowed as word target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
@ -1494,7 +1664,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val idx = target.constArrayIndexValue!! * 2
|
val idx = target.constArrayIndexValue!! * 2u
|
||||||
when (regs) {
|
when (regs) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
|
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
|
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
|
||||||
@ -1624,7 +1794,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" stz ${target.asmVarname} | stz ${target.asmVarname}+1")
|
asmgen.out(" stz ${target.asmVarname} | stz ${target.asmVarname}+1")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
|
throw AssemblyError("memory is bytes not words")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
||||||
@ -1715,15 +1885,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
|
private fun assignConstantByte(target: AsmAssignTarget, byte: Int) {
|
||||||
if(byte==0.toShort() && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
if(byte==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
// optimize setting zero value for this cpu
|
// optimize setting zero value for this cpu
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out(" stz ${target.asmVarname} ")
|
asmgen.out(" stz ${target.asmVarname} ")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
asmgen.out(" lda #${byte.toHex()}")
|
asmgen.out(" lda #0")
|
||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
@ -1869,7 +2039,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
||||||
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
||||||
@ -1877,27 +2047,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-zero value
|
// non-zero value
|
||||||
val constFloat = asmgen.getFloatAsmConst(float)
|
val constFloat = allocator.getFloatAsmConst(float)
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $constFloat
|
lda #<$constFloat
|
||||||
sta ${target.asmVarname}
|
ldy #>$constFloat
|
||||||
lda $constFloat+1
|
sta P8ZP_SCRATCH_W1
|
||||||
sta ${target.asmVarname}+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda $constFloat+2
|
lda #<${target.asmVarname}
|
||||||
sta ${target.asmVarname}+2
|
ldy #>${target.asmVarname}
|
||||||
lda $constFloat+3
|
jsr floats.copy_float""")
|
||||||
sta ${target.asmVarname}+3
|
|
||||||
lda $constFloat+4
|
|
||||||
sta ${target.asmVarname}+4
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val arrayVarName = target.asmVarname
|
val arrayVarName = target.asmVarname
|
||||||
@ -1905,17 +2071,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
if (constIndex!=null) {
|
if (constIndex!=null) {
|
||||||
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
|
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $constFloat
|
lda #<$constFloat
|
||||||
sta $arrayVarName+$indexValue
|
ldy #>$constFloat
|
||||||
lda $constFloat+1
|
sta P8ZP_SCRATCH_W1
|
||||||
sta $arrayVarName+$indexValue+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda $constFloat+2
|
lda #<($arrayVarName+$indexValue)
|
||||||
sta $arrayVarName+$indexValue+2
|
ldy #>($arrayVarName+$indexValue)
|
||||||
lda $constFloat+3
|
jsr floats.copy_float""")
|
||||||
sta $arrayVarName+$indexValue+3
|
|
||||||
lda $constFloat+4
|
|
||||||
sta $arrayVarName+$indexValue+4
|
|
||||||
""")
|
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1934,7 +2096,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
||||||
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
||||||
@ -1942,14 +2104,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignMemoryByte(target: AsmAssignTarget, address: Int?, identifier: IdentifierReference?) {
|
private fun assignMemoryByte(target: AsmAssignTarget, address: UInt?, identifier: IdentifierReference?) {
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -2033,7 +2195,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: Int?, identifier: IdentifierReference?) {
|
private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: UInt?, identifier: IdentifierReference?) {
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
when(wordtarget.kind) {
|
when(wordtarget.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -2098,58 +2260,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
|
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
|
||||||
val addressExpr = memoryAddress.addressExpression
|
val addressExpr = memoryAddress.addressExpression
|
||||||
val addressLv = addressExpr as? NumericLiteralValue
|
val addressLv = addressExpr as? NumericLiteral
|
||||||
|
|
||||||
fun storeViaExprEval() {
|
fun storeViaExprEval() {
|
||||||
when(addressExpr) {
|
when(addressExpr) {
|
||||||
is NumericLiteralValue, is IdentifierReference -> {
|
is NumericLiteral, is IdentifierReference -> {
|
||||||
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
|
||||||
else
|
|
||||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// same as above but we need to save the A register
|
// same as above but we need to save the A register
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
|
||||||
else
|
|
||||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun storeAIntoPointerVar(pointervar: IdentifierReference) {
|
|
||||||
val sourceName = asmgen.asmVariableName(pointervar)
|
|
||||||
val vardecl = pointervar.targetVarDecl(program)!!
|
|
||||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
|
||||||
if (asmgen.isZpVar(scopedName)) {
|
|
||||||
// pointervar is already in the zero page, no need to copy
|
|
||||||
asmgen.out(" sta ($sourceName)")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $sourceName
|
|
||||||
sty P8ZP_SCRATCH_W2
|
|
||||||
ldy $sourceName+1
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
sta (P8ZP_SCRATCH_W2)""")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (asmgen.isZpVar(scopedName)) {
|
|
||||||
// pointervar is already in the zero page, no need to copy
|
|
||||||
asmgen.out(" ldy #0 | sta ($sourceName),y")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $sourceName
|
|
||||||
sty P8ZP_SCRATCH_W2
|
|
||||||
ldy $sourceName+1
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
ldy #0
|
|
||||||
sta (P8ZP_SCRATCH_W2),y""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2159,7 +2283,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" sta ${addressLv.number.toHex()}")
|
asmgen.out(" sta ${addressLv.number.toHex()}")
|
||||||
}
|
}
|
||||||
addressExpr is IdentifierReference -> {
|
addressExpr is IdentifierReference -> {
|
||||||
storeAIntoPointerVar(addressExpr)
|
asmgen.storeAIntoPointerVar(addressExpr)
|
||||||
}
|
}
|
||||||
addressExpr is BinaryExpression -> {
|
addressExpr is BinaryExpression -> {
|
||||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
||||||
@ -2177,10 +2301,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
|
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
if(expr.inferType(program) istype DataType.FLOAT && dt!=DataType.FLOAT) {
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
} else {
|
||||||
translateNormalAssignment(assign)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
||||||
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
|
translateNormalAssignment(assign)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
@ -1,19 +1,18 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.code.core.*
|
||||||
import prog8.compiler.target.AssemblyError
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
|
||||||
import prog8.compilerinterface.CpuType
|
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
private val exprAsmGen: ExpressionsAsmGen,
|
private val asmgen: AsmGen,
|
||||||
private val asmgen: AsmGen
|
private val allocator: VariableAllocator
|
||||||
) {
|
) {
|
||||||
fun translate(assign: AsmAssignment) {
|
fun translate(assign: AsmAssignment) {
|
||||||
require(assign.isAugmentable)
|
require(assign.isAugmentable)
|
||||||
@ -22,15 +21,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
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 itype = value.inferType(program)
|
||||||
if(!itype.isKnown)
|
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val type = itype.getOr(DataType.UNDEFINED)
|
|
||||||
when (value.operator) {
|
when (value.operator) {
|
||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> inplaceNegate(assign.target, type)
|
"-" -> inplaceNegate(target, type)
|
||||||
"~" -> inplaceInvert(assign.target, type)
|
"~" -> inplaceInvert(target, type)
|
||||||
"not" -> inplaceBooleanNot(assign.target, type)
|
"not" -> inplaceBooleanNot(target, type)
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +45,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
return inplaceModification(target, binExpr.operator, binExpr.right)
|
return inplaceModification(target, binExpr.operator, binExpr.right)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binExpr.operator in associativeOperators) {
|
if (binExpr.operator in AssociativeOperators) {
|
||||||
if (binExpr.right isSameAs astTarget) {
|
if (binExpr.right isSameAs astTarget) {
|
||||||
// A = 5 <operator> A
|
// A = 5 <operator> A
|
||||||
return inplaceModification(target, binExpr.operator, binExpr.left)
|
return inplaceModification(target, binExpr.operator, binExpr.left)
|
||||||
@ -102,11 +100,67 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FatalAstException("assignment should be augmentable $binExpr")
|
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||||
|
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||||
|
if(leftBinExpr!=null && rightBinExpr==null) {
|
||||||
|
if(leftBinExpr.left isSameAs astTarget) {
|
||||||
|
// X = (X <oper> Right) <oper> Something
|
||||||
|
inplaceModification(target, leftBinExpr.operator, leftBinExpr.right)
|
||||||
|
inplaceModification(target, binExpr.operator, binExpr.right)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(leftBinExpr.right isSameAs astTarget) {
|
||||||
|
// X = (Left <oper> X) <oper> Something
|
||||||
|
if(leftBinExpr.operator in AssociativeOperators) {
|
||||||
|
inplaceModification(target, leftBinExpr.operator, leftBinExpr.left)
|
||||||
|
inplaceModification(target, binExpr.operator, binExpr.right)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("operands in wrong order for non-associative operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(leftBinExpr==null && rightBinExpr!=null) {
|
||||||
|
if(rightBinExpr.left isSameAs astTarget) {
|
||||||
|
// X = Something <oper> (X <oper> Right)
|
||||||
|
if(binExpr.operator in AssociativeOperators) {
|
||||||
|
inplaceModification(target, rightBinExpr.operator, rightBinExpr.right)
|
||||||
|
inplaceModification(target, binExpr.operator, binExpr.left)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("operands in wrong order for non-associative operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rightBinExpr.right isSameAs astTarget) {
|
||||||
|
// X = Something <oper> (Left <oper> X)
|
||||||
|
if(binExpr.operator in AssociativeOperators && rightBinExpr.operator in AssociativeOperators) {
|
||||||
|
inplaceModification(target, rightBinExpr.operator, rightBinExpr.left)
|
||||||
|
inplaceModification(target, binExpr.operator, binExpr.left)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("operands in wrong order for non-associative operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FatalAstException("assignment should follow augmentable rules $binExpr")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification(target: AsmAssignTarget, operator: String, value: Expression) {
|
private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: Expression) {
|
||||||
val valueLv = (value as? NumericLiteralValue)?.number
|
|
||||||
|
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
||||||
|
// it will create the most optimized code to do this (so it type-extends for us).
|
||||||
|
// But we can't deal with writing a word into a byte - explicit typeconversion is required
|
||||||
|
val value = if(program.memsizer.memorySize(origValue.inferType(program).getOrElse { throw AssemblyError("unknown dt") }) > program.memsizer.memorySize(target.datatype)) {
|
||||||
|
val typecast = TypecastExpression(origValue, target.datatype, true, origValue.position)
|
||||||
|
typecast.linkParents(origValue.parent)
|
||||||
|
typecast
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
origValue
|
||||||
|
}
|
||||||
|
|
||||||
|
val valueLv = (value as? NumericLiteral)?.number
|
||||||
val ident = value as? IdentifierReference
|
val ident = value as? IdentifierReference
|
||||||
val memread = value as? DirectMemoryRead
|
val memread = value as? DirectMemoryRead
|
||||||
|
|
||||||
@ -119,7 +173,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
else -> inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
@ -131,7 +185,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator))
|
||||||
|
return
|
||||||
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
else -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
@ -142,7 +197,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
|
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
|
||||||
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
|
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
||||||
@ -154,15 +209,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
val memory = target.memory!!
|
val memory = target.memory!!
|
||||||
when (memory.addressExpression) {
|
when (memory.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val addr = (memory.addressExpression as NumericLiteralValue).number.toInt()
|
val addr = (memory.addressExpression as NumericLiteral).number.toInt()
|
||||||
// re-use code to assign a variable, instead this time, use a direct memory address
|
// re-use code to assign a variable, instead this time, use a direct memory address
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
@ -174,33 +229,33 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
else -> inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO OTHER EVALUATION HERE, don't use the estack
|
// 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, program, asmgen, DataType.UWORD, memory.definingSubroutine))
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") // TODO don't use estack to transfer the address to read from
|
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())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
|
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx") // TODO don't use estack to transfer the address to read from
|
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
with(target.array!!.indexer) {
|
with(target.array!!.indexer) {
|
||||||
val indexNum = indexExpr as? NumericLiteralValue
|
val indexNum = indexExpr as? NumericLiteral
|
||||||
val indexVar = indexExpr as? IdentifierReference
|
val indexVar = indexExpr as? IdentifierReference
|
||||||
when {
|
when {
|
||||||
indexNum!=null -> {
|
indexNum!=null -> {
|
||||||
@ -212,7 +267,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
@ -224,7 +279,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
@ -235,7 +290,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
|
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
|
||||||
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
|
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
||||||
@ -289,17 +344,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg in-place modification")
|
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack in-place modification")
|
TargetStorageKind.STACK -> throw AssemblyError("no asm gen for stack in-place modification")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
|
private fun tryInplaceModifyWithRemovedRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
|
||||||
if (target.datatype == value.type) {
|
if (target.datatype == value.type) {
|
||||||
val childIDt = value.expression.inferType(program)
|
val childIDt = value.expression.inferType(program)
|
||||||
if(!childIDt.isKnown)
|
val childDt = childIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val childDt = childIDt.getOr(DataType.UNDEFINED)
|
|
||||||
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
||||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||||
// (works for integer types, not for float.)
|
// (works for integer types, not for float.)
|
||||||
@ -312,13 +365,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) {
|
private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) {
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
|
"+" -> asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
|
||||||
"-" -> asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
|
"-" -> asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
|
||||||
"*" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.multiply_bytes | ldy #0")
|
"*" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.multiply_bytes")
|
||||||
"/" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | tya | ldy #0")
|
"/" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | tya")
|
||||||
"%" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | ldy #0")
|
"%" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm")
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy P8ZP_SCRATCH_B1
|
ldy P8ZP_SCRATCH_B1
|
||||||
@ -342,8 +396,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
"^", "xor" -> 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")
|
||||||
}
|
}
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
||||||
@ -354,9 +407,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> asmgen.out(" clc | adc $otherName")
|
"+" -> asmgen.out(" clc | adc $otherName")
|
||||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||||
"*" -> asmgen.out(" ldy $otherName | jsr math.multiply_bytes | ldy #0")
|
"*" -> asmgen.out(" ldy $otherName | jsr math.multiply_bytes")
|
||||||
"/" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | tya | ldy #0")
|
"/" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | tya")
|
||||||
"%" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | ldy #0")
|
"%" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm")
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -380,7 +433,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"^", "xor" -> asmgen.out(" eor $otherName")
|
"^", "xor" -> 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.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_litval_to_pointer(pointervar: IdentifierReference, operator: String, value: Int) {
|
private fun inplaceModification_byte_litval_to_pointer(pointervar: IdentifierReference, operator: String, value: Int) {
|
||||||
@ -389,63 +442,63 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"+" -> {
|
"+" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" clc | adc #$value")
|
asmgen.out(" clc | adc #$value")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" sec | sbc #$value")
|
asmgen.out(" sec | sbc #$value")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value in asmgen.optimizedByteMultiplications)
|
if(value in asmgen.optimizedByteMultiplications)
|
||||||
asmgen.out(" jsr math.mul_byte_${value}")
|
asmgen.out(" jsr math.mul_byte_${value}")
|
||||||
else
|
else
|
||||||
asmgen.out(" ldy #$value | jsr math.multiply_bytes | ldy #0")
|
asmgen.out(" ldy #$value | jsr math.multiply_bytes")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value==0)
|
if(value==0)
|
||||||
throw AssemblyError("division by zero")
|
throw AssemblyError("division by zero")
|
||||||
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm | tya | ldy #0")
|
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm | tya")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"%" -> {
|
"%" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value==0)
|
if(value==0)
|
||||||
throw AssemblyError("division by zero")
|
throw AssemblyError("division by zero")
|
||||||
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm | ldy #0")
|
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
repeat(value) { asmgen.out(" asl a") }
|
repeat(value) { asmgen.out(" asl a") }
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
repeat(value) { asmgen.out(" lsr a") }
|
repeat(value) { asmgen.out(" lsr a") }
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&", "and" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" and #$value")
|
asmgen.out(" and #$value")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|", "or" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" ora #$value")
|
asmgen.out(" ora #$value")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" eor #$value")
|
asmgen.out(" eor #$value")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
@ -522,6 +575,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
|
"==" -> {
|
||||||
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $name
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $name
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,6 +652,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||||
|
"==" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $otherName
|
||||||
|
cmp $name
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
bne ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $otherName
|
||||||
|
cmp $name
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,6 +743,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||||
|
"==" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
cmp #$value
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
cmp #$value
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta $name""")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,14 +770,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc $name
|
adc $name
|
||||||
sta $name""")
|
sta $name""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda $name
|
lda $name
|
||||||
@ -673,15 +786,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta $name""")
|
sta $name""")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|", "or" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&", "and" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
// TODO: tuned code for more operators
|
// TODO: tuned code for more operators
|
||||||
@ -694,7 +807,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc $name
|
adc $name
|
||||||
@ -704,7 +817,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda $name
|
lda $name
|
||||||
@ -716,11 +829,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"|", "or" -> {
|
"|", "or" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" ora $name | sta $name")
|
asmgen.out(" ora $name | sta $name")
|
||||||
}
|
}
|
||||||
"&", "and" -> {
|
"&", "and" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(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) {
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
@ -730,7 +843,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
// TODO: tuned code for more operators
|
// TODO: tuned code for more operators
|
||||||
@ -954,6 +1067,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
}
|
}
|
||||||
|
value == 0x00ff -> {
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz $name+1")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
|
}
|
||||||
|
value == 0xff00 -> {
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz $name")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta $name")
|
||||||
|
}
|
||||||
value and 255 == 0 -> {
|
value and 255 == 0 -> {
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name")
|
asmgen.out(" stz $name")
|
||||||
@ -1033,33 +1158,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #255
|
||||||
lda $otherName
|
lda $otherName
|
||||||
bpl +
|
bpl +
|
||||||
dey ; sign extend
|
iny ; sign extend
|
||||||
+ sty P8ZP_SCRATCH_B1
|
+ eor #255
|
||||||
lda $name
|
|
||||||
sec
|
sec
|
||||||
sbc $otherName
|
adc $name
|
||||||
sta $name
|
sta $name
|
||||||
lda $name+1
|
tya
|
||||||
sbc P8ZP_SCRATCH_B1
|
adc $name+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
if(valueDt==DataType.UBYTE) {
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
||||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
else
|
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
else
|
||||||
|
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" lda $otherName")
|
||||||
|
asmgen.signExtendAYlsb(valueDt)
|
||||||
|
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
|
||||||
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
ldy $name+1
|
ldy $name+1
|
||||||
jsr math.multiply_words
|
jsr math.multiply_words
|
||||||
lda math.multiply_words.result
|
lda math.multiply_words.result
|
||||||
sta $name
|
sta $name
|
||||||
lda math.multiply_words.result+1
|
lda math.multiply_words.result+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
if(dt==DataType.UWORD) {
|
if(dt==DataType.UWORD) {
|
||||||
@ -1103,7 +1233,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta $name
|
sta $name
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta $name+1
|
sta $name+1
|
||||||
""") }
|
""")
|
||||||
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -1235,9 +1366,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
|
|
||||||
val valueiDt = value.inferType(program)
|
val valueiDt = value.inferType(program)
|
||||||
if(!valueiDt.isKnown)
|
val valueDt = valueiDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val valueDt = valueiDt.getOr(DataType.UNDEFINED)
|
|
||||||
|
|
||||||
fun multiplyVarByWordInAY() {
|
fun multiplyVarByWordInAY() {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1316,29 +1445,28 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
|
||||||
if(valueDt==DataType.UBYTE)
|
if(valueDt==DataType.UBYTE)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_REG
|
sbc P8ZP_SCRATCH_B1
|
||||||
sta $name
|
sta $name
|
||||||
bcs +
|
bcs +
|
||||||
dec $name+1
|
dec $name+1
|
||||||
+""")
|
+""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #255
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_B1
|
||||||
bpl +
|
bpl +
|
||||||
dey ; sign extend
|
iny ; sign extend
|
||||||
+ sty P8ZP_SCRATCH_B1
|
+ eor #255
|
||||||
lda $name
|
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_REG
|
adc $name
|
||||||
sta $name
|
sta $name
|
||||||
lda $name+1
|
tya
|
||||||
sbc P8ZP_SCRATCH_B1
|
adc $name+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
@ -1462,14 +1590,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
jsr floats.FPWRT
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
@ -1516,28 +1636,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val otherName = asmgen.asmVariableName(ident)
|
val otherName = asmgen.asmVariableName(ident)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
|
||||||
if(asmgen.haveFPWR()) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$otherName
|
|
||||||
ldy #>$otherName
|
|
||||||
jsr floats.FPWR
|
|
||||||
""")
|
|
||||||
} else
|
|
||||||
// cx16 doesn't have FPWR() only FPWRT()
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$otherName
|
|
||||||
ldy #>$otherName
|
|
||||||
jsr floats.MOVFM
|
|
||||||
jsr floats.FPWRT
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
@ -1590,31 +1688,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
|
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
|
||||||
val constValueName = asmgen.getFloatAsmConst(value)
|
val constValueName = allocator.getFloatAsmConst(value)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
|
||||||
if(asmgen.haveFPWR()) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$constValueName
|
|
||||||
ldy #>$constValueName
|
|
||||||
jsr floats.FPWR
|
|
||||||
""")
|
|
||||||
} else
|
|
||||||
// cx16 doesn't have FPWR() only FPWRT()
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$constValueName
|
|
||||||
ldy #>$constValueName
|
|
||||||
jsr floats.MOVFM
|
|
||||||
jsr floats.FPWRT
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (value == 0.0)
|
if (value == 0.0)
|
||||||
return
|
return
|
||||||
@ -1739,8 +1815,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
val mem = target.memory!!
|
val mem = target.memory!!
|
||||||
when (mem.addressExpression) {
|
when (mem.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val addr = (mem.addressExpression as NumericLiteralValue).number.toHex()
|
val addr = (mem.addressExpression as NumericLiteral).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $addr
|
lda $addr
|
||||||
beq +
|
beq +
|
||||||
@ -1754,17 +1830,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
beq +
|
beq +
|
||||||
lda #1
|
lda #1
|
||||||
+ eor #1""")
|
+ eor #1""")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
|
asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
|
||||||
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
|
||||||
lda (P8ZP_SCRATCH_W2),y
|
|
||||||
beq +
|
beq +
|
||||||
lda #1
|
lda #1
|
||||||
+ eor #1
|
+ eor #1""")
|
||||||
sta (P8ZP_SCRATCH_W2),y""")
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1774,24 +1849,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
lda #1
|
lda #1
|
||||||
+ eor #1""")
|
+ eor #1""")
|
||||||
RegisterOrPair.X -> asmgen.out("""
|
RegisterOrPair.X -> asmgen.out("""
|
||||||
txa
|
txa
|
||||||
beq +
|
beq +
|
||||||
lda #1
|
lda #1
|
||||||
+ eor #1
|
+ eor #1
|
||||||
tax""")
|
tax""")
|
||||||
RegisterOrPair.Y -> asmgen.out("""
|
RegisterOrPair.Y -> asmgen.out("""
|
||||||
tya
|
tya
|
||||||
beq +
|
beq +
|
||||||
lda #1
|
lda #1
|
||||||
+ eor #1
|
+ eor #1
|
||||||
tay""")
|
tay""")
|
||||||
else -> throw AssemblyError("invalid reg dt for byte not")
|
else -> throw AssemblyError("invalid reg dt for byte not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for byte stack not")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack not")
|
||||||
else -> throw AssemblyError("missing codegen for in-place not of ubyte ${target.kind}")
|
else -> throw AssemblyError("no asm gen for in-place not of ubyte ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1843,13 +1918,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+ ldx #1
|
+ ldx #1
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
in Cx16VirtualRegisters -> TODO()
|
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
||||||
else -> throw AssemblyError("invalid reg dt for word not")
|
else -> throw AssemblyError("invalid reg dt for word not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> TODO("no asm gen for uword-memory not")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack not")
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for word stack not")
|
else -> throw AssemblyError("no asm gen for in-place not of uword for ${target.kind}")
|
||||||
else -> throw AssemblyError("missing codegen for in-place not of uword for ${target.kind}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("boolean-not of invalid type")
|
else -> throw AssemblyError("boolean-not of invalid type")
|
||||||
@ -1869,8 +1943,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
val memory = target.memory!!
|
val memory = target.memory!!
|
||||||
when (memory.addressExpression) {
|
when (memory.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteral -> {
|
||||||
val addr = (memory.addressExpression as NumericLiteralValue).number.toHex()
|
val addr = (memory.addressExpression as NumericLiteral).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $addr
|
lda $addr
|
||||||
eor #255
|
eor #255
|
||||||
@ -1886,8 +1960,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (P8ZP_SCRATCH_W2),y
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
eor #255
|
eor #255""")
|
||||||
sta (P8ZP_SCRATCH_W2),y""")
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1899,8 +1973,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("invalid reg dt for byte invert")
|
else -> throw AssemblyError("invalid reg dt for byte invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for byte stack invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
||||||
else -> throw AssemblyError("missing codegen for in-place invert ubyte for ${target.kind}")
|
else -> throw AssemblyError("no asm gen for in-place invert ubyte for ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1919,15 +1993,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
|
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
|
||||||
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
|
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
|
||||||
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
|
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
||||||
TODO("codegen for cx16 word register invert")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid reg dt for word invert")
|
else -> throw AssemblyError("invalid reg dt for word invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> TODO("no asm gen for uword-memory invert")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for word stack invert")
|
else -> throw AssemblyError("no asm gen for in-place invert uword for ${target.kind}")
|
||||||
else -> throw AssemblyError("missing codegen for in-place invert uword for ${target.kind}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
else -> throw AssemblyError("invert of invalid type")
|
||||||
@ -1947,15 +2018,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.A -> asmgen.out(" sta P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1")
|
RegisterOrPair.A -> {
|
||||||
RegisterOrPair.X -> asmgen.out(" stx P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1 | tax")
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
RegisterOrPair.Y -> asmgen.out(" sty P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1 | tay")
|
asmgen.out(" eor #255 | ina")
|
||||||
|
else
|
||||||
|
asmgen.out(" eor #255 | clc | adc #1")
|
||||||
|
|
||||||
|
}
|
||||||
|
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")
|
||||||
else -> throw AssemblyError("invalid reg dt for byte negate")
|
else -> throw AssemblyError("invalid reg dt for byte negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> TODO("can't in-place negate memory ubyte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't in-place negate")
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for byte stack negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
||||||
else -> throw AssemblyError("missing codegen for in-place negate byte array")
|
else -> throw AssemblyError("no asm gen for in-place negate byte")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -1974,51 +2051,46 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
when(target.register!!) { //P8ZP_SCRATCH_REG
|
when(target.register!!) { //P8ZP_SCRATCH_REG
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
|
||||||
stx P8ZP_SCRATCH_REG+1
|
|
||||||
lda #0
|
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_REG
|
eor #255
|
||||||
|
adc #0
|
||||||
pha
|
pha
|
||||||
lda #0
|
txa
|
||||||
sbc P8ZP_SCRATCH_REG+1
|
eor #255
|
||||||
|
adc #0
|
||||||
tax
|
tax
|
||||||
pla""")
|
pla""")
|
||||||
}
|
}
|
||||||
RegisterOrPair.AY -> {
|
RegisterOrPair.AY -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
|
||||||
sty P8ZP_SCRATCH_REG+1
|
|
||||||
lda #0
|
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_REG
|
eor #255
|
||||||
|
adc #0
|
||||||
pha
|
pha
|
||||||
lda #0
|
tya
|
||||||
sbc P8ZP_SCRATCH_REG+1
|
eor #255
|
||||||
|
adc #0
|
||||||
tay
|
tay
|
||||||
pla""")
|
pla""")
|
||||||
}
|
}
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
sty P8ZP_SCRATCH_REG+1
|
|
||||||
lda #0
|
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_REG
|
txa
|
||||||
|
eor #255
|
||||||
|
adc #0
|
||||||
tax
|
tax
|
||||||
lda #0
|
tya
|
||||||
sbc P8ZP_SCRATCH_REG+1
|
eor #255
|
||||||
|
adc #0
|
||||||
tay""")
|
tay""")
|
||||||
}
|
}
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
||||||
TODO("codegen for cx16 word register negate")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid reg dt for word neg")
|
else -> throw AssemblyError("invalid reg dt for word neg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> TODO("no asm gen for word memory negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for word stack negate")
|
else -> throw AssemblyError("no asm gen for in-place negate word")
|
||||||
else -> throw AssemblyError("missing codegen for in-place negate word array")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -2031,13 +2103,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1
|
sta ${target.asmVarname}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> TODO("missing codegen for float reg negate")
|
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
||||||
TargetStorageKind.MEMORY -> TODO("missing codegen for float memory negate")
|
|
||||||
TargetStorageKind.STACK -> TODO("missing codegen for stack float negate")
|
|
||||||
else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}")
|
else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("negate of invalid type")
|
else -> throw AssemblyError("negate of invalid type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
codeGenExperimental/build.gradle
Normal file
46
codeGenExperimental/build.gradle
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':codeAst')
|
||||||
|
implementation project(':codeCore')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
16
codeGenExperimental/codeGenExperimental.iml
Normal file
16
codeGenExperimental/codeGenExperimental.iml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?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" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<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" />
|
||||||
|
</component>
|
||||||
|
</module>
|
39
codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt
Normal file
39
codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,666 @@
|
|||||||
|
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.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.joinToString(",") { it.name })
|
||||||
|
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,89 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
47
codeGenVirtual/build.gradle
Normal file
47
codeGenVirtual/build.gradle
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':virtualmachine')
|
||||||
|
implementation project(':codeAst')
|
||||||
|
implementation project(':codeCore')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
16
codeGenVirtual/codeGenVirtual.iml
Normal file
16
codeGenVirtual/codeGenVirtual.iml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?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" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<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>
|
||||||
|
</module>
|
138
codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt
Normal file
138
codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.vm.Instruction
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.OpcodesWithAddress
|
||||||
|
import prog8.vm.VmDataType
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.nio.file.Path
|
||||||
|
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")
|
||||||
|
|
||||||
|
if(!options.dontReinitGlobals) {
|
||||||
|
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")
|
||||||
|
is VmCodeInlineAsm -> {
|
||||||
|
val asm = line.assembly.replace("""\{[a-zA-Z\d_\.]+\}""".toRegex()) { matchResult ->
|
||||||
|
val name = matchResult.value.substring(1, matchResult.value.length-1).split('.')
|
||||||
|
allocations.get(name).toString() }
|
||||||
|
write(asm+"\n")
|
||||||
|
}
|
||||||
|
is VmCodeInlineBinary -> {
|
||||||
|
write("incbin \"${line.file}\"")
|
||||||
|
if(line.offset!=null)
|
||||||
|
write(",${line.offset}")
|
||||||
|
if(line.length!=null)
|
||||||
|
write(",${line.length}")
|
||||||
|
write("\n")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid vm code line")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
fpReg1: Int?=null, // 0-$ffff
|
||||||
|
fpReg2: Int?=null, // 0-$ffff
|
||||||
|
value: Int?=null, // 0-$ffff
|
||||||
|
fpValue: Float?=null,
|
||||||
|
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
|
||||||
|
): VmCodeLine() {
|
||||||
|
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(reg1!=null && (reg1<0 || reg1>65536))
|
||||||
|
throw IllegalArgumentException("reg1 out of bounds")
|
||||||
|
if(reg2!=null && (reg2<0 || reg2>65536))
|
||||||
|
throw IllegalArgumentException("reg2 out of bounds")
|
||||||
|
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
|
||||||
|
throw IllegalArgumentException("fpReg1 out of bounds")
|
||||||
|
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
|
||||||
|
throw IllegalArgumentException("fpReg2 out of bounds")
|
||||||
|
|
||||||
|
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||||
|
when (type) {
|
||||||
|
VmDataType.BYTE -> {
|
||||||
|
if (value < -128 || value > 255)
|
||||||
|
throw IllegalArgumentException("value out of range for byte: $value")
|
||||||
|
}
|
||||||
|
VmDataType.WORD -> {
|
||||||
|
if (value < -32768 || value > 65535)
|
||||||
|
throw IllegalArgumentException("value out of range for word: $value")
|
||||||
|
}
|
||||||
|
VmDataType.FLOAT, null -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class VmCodeInlineAsm(asm: String): VmCodeLine() {
|
||||||
|
val assembly: String = asm.trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class VmCodeInlineBinary(val file: Path, val offset: UInt?, val length: UInt?): VmCodeLine()
|
232
codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt
Normal file
232
codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.SignedDatatypes
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.VmDataType
|
||||||
|
|
||||||
|
internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) {
|
||||||
|
|
||||||
|
internal fun translate(assignment: PtAssignment): VmCodeChunk {
|
||||||
|
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): VmCodeChunk {
|
||||||
|
val ident = assignment.target.identifier
|
||||||
|
val memory = assignment.target.memory
|
||||||
|
val array = assignment.target.array
|
||||||
|
|
||||||
|
return if(ident!=null) {
|
||||||
|
val address = codeGen.allocations.get(ident.targetName)
|
||||||
|
assignSelfInMemory(address, assignment.value, assignment)
|
||||||
|
} else if(memory != null) {
|
||||||
|
if(memory.address is PtNumber)
|
||||||
|
assignSelfInMemory((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
|
||||||
|
else
|
||||||
|
fallbackAssign(assignment)
|
||||||
|
} else if(array!=null) {
|
||||||
|
// TODO in-place array element assignment?
|
||||||
|
fallbackAssign(assignment)
|
||||||
|
} else {
|
||||||
|
fallbackAssign(assignment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignSelfInMemory(
|
||||||
|
address: Int,
|
||||||
|
value: PtExpression,
|
||||||
|
origAssign: PtAssignment
|
||||||
|
): VmCodeChunk {
|
||||||
|
val vmDt = codeGen.vmType(value.type)
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
when(value) {
|
||||||
|
is PtIdentifier -> return code // do nothing, x=x null assignment.
|
||||||
|
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
|
||||||
|
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address)
|
||||||
|
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, origAssign)
|
||||||
|
is PtMemoryByte -> {
|
||||||
|
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
|
||||||
|
code // do nothing, mem=mem null assignment.
|
||||||
|
else {
|
||||||
|
// read and write a (i/o) memory location to itself.
|
||||||
|
val tempReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
||||||
|
code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> return fallbackAssign(origAssign)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fallbackAssign(origAssign: PtAssignment): VmCodeChunk {
|
||||||
|
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: VmDataType,
|
||||||
|
signed: Boolean,
|
||||||
|
address: Int,
|
||||||
|
origAssign: PtAssignment
|
||||||
|
): VmCodeChunk {
|
||||||
|
when(operator) {
|
||||||
|
"+" -> return expressionEval.operatorPlusInplace(address, vmDt, operand)
|
||||||
|
"-" -> return expressionEval.operatorMinusInplace(address, vmDt, operand)
|
||||||
|
"*" -> return expressionEval.operatorMultiplyInplace(address, vmDt, operand)
|
||||||
|
"/" -> return expressionEval.operatorDivideInplace(address, vmDt, signed, operand)
|
||||||
|
"|" -> return expressionEval.operatorOrInplace(address, vmDt, operand)
|
||||||
|
"&" -> return expressionEval.operatorAndInplace(address, vmDt, operand)
|
||||||
|
"^" -> return expressionEval.operatorXorInplace(address, vmDt, operand)
|
||||||
|
"<<" -> return expressionEval.operatorShiftLeftInplace(address, vmDt, operand)
|
||||||
|
">>" -> return expressionEval.operatorShiftRightInplace(address, vmDt, signed, operand)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return fallbackAssign(origAssign)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inplacePrefix(operator: String, vmDt: VmDataType, address: Int): VmCodeChunk {
|
||||||
|
val code= VmCodeChunk()
|
||||||
|
when(operator) {
|
||||||
|
"+" -> { }
|
||||||
|
"-" -> {
|
||||||
|
code += VmCodeInstruction(Opcode.NEGM, vmDt, value = address)
|
||||||
|
}
|
||||||
|
"~" -> {
|
||||||
|
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.XORM, vmDt, reg1=regMask, value = address)
|
||||||
|
}
|
||||||
|
"not" -> {
|
||||||
|
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateRegularAssign(assignment: PtAssignment): VmCodeChunk {
|
||||||
|
// 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.vmType(assignment.value.type)
|
||||||
|
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
var resultRegister = -1
|
||||||
|
var resultFpRegister = -1
|
||||||
|
val zero = codeGen.isZero(assignment.value)
|
||||||
|
if(!zero) {
|
||||||
|
// calculate the assignment value
|
||||||
|
if (vmDt == VmDataType.FLOAT) {
|
||||||
|
resultFpRegister = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
|
||||||
|
} else {
|
||||||
|
resultRegister = if (assignment.value is PtMachineRegister) {
|
||||||
|
(assignment.value as PtMachineRegister).register
|
||||||
|
} else {
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(assignment.value, reg, -1)
|
||||||
|
reg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ident!=null) {
|
||||||
|
val address = codeGen.allocations.get(ident.targetName)
|
||||||
|
code += if(zero) {
|
||||||
|
VmCodeInstruction(Opcode.STOREZM, vmDt, value = address)
|
||||||
|
} else {
|
||||||
|
if (vmDt == VmDataType.FLOAT)
|
||||||
|
VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value = address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(array!=null) {
|
||||||
|
val variable = array.variable.targetName
|
||||||
|
var variableAddr = codeGen.allocations.get(variable)
|
||||||
|
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
||||||
|
val fixedIndex = constIntValue(array.index)
|
||||||
|
if(zero) {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
variableAddr += fixedIndex*itemsize
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=variableAddr)
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, value=variableAddr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(vmDt== VmDataType.FLOAT) {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
variableAddr += fixedIndex*itemsize
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value=variableAddr)
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
variableAddr += fixedIndex*itemsize
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value=variableAddr)
|
||||||
|
} else {
|
||||||
|
val indexReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += loadIndexReg(array, itemsize, indexReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(memory!=null) {
|
||||||
|
require(vmDt== VmDataType.BYTE)
|
||||||
|
if(zero) {
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw AssemblyError("weird assigntarget")
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(itemsize==1) {
|
||||||
|
code += 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)
|
||||||
|
code += expressionEval.translateExpression(mult, indexReg, -1)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
}
|
390
codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt
Normal file
390
codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
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 {
|
||||||
|
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" -> VmCodeChunk() // vm doesn't have registers to save/restore
|
||||||
|
"rnd" -> funcRnd(resultRegister)
|
||||||
|
"rndw" -> funcRndw(resultRegister)
|
||||||
|
"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" -> VmCodeChunk()
|
||||||
|
"mkword" -> funcMkword(call, resultRegister)
|
||||||
|
"sort" -> funcSort(call)
|
||||||
|
"reverse" -> funcReverse(call)
|
||||||
|
"swap" -> funcSwap(call)
|
||||||
|
"rol" -> funcRolRor2(Opcode.ROXL, call, resultRegister)
|
||||||
|
"ror" -> funcRolRor2(Opcode.ROXR, call, resultRegister)
|
||||||
|
"rol2" -> funcRolRor2(Opcode.ROL, call, resultRegister)
|
||||||
|
"ror2" -> funcRolRor2(Opcode.ROR, call, resultRegister)
|
||||||
|
else -> TODO("builtinfunc ${call.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val leftRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
val rightRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args[0], leftRegister, -1)
|
||||||
|
code += exprGen.translateExpression(call.args[1], rightRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val syscall =
|
||||||
|
when (array.dt) {
|
||||||
|
DataType.ARRAY_UB,
|
||||||
|
DataType.ARRAY_B -> Syscall.ANY_BYTE
|
||||||
|
DataType.ARRAY_UW,
|
||||||
|
DataType.ARRAY_W -> Syscall.ANY_WORD
|
||||||
|
DataType.ARRAY_F -> Syscall.ANY_FLOAT
|
||||||
|
else -> throw IllegalArgumentException("weird type")
|
||||||
|
}
|
||||||
|
code += exprGen.translateExpression(call.args[0], 0, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
|
||||||
|
code += VmCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal)
|
||||||
|
if (resultRegister != 0)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||||
|
val syscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB,
|
||||||
|
DataType.ARRAY_B -> Syscall.ALL_BYTE
|
||||||
|
DataType.ARRAY_UW,
|
||||||
|
DataType.ARRAY_W -> Syscall.ALL_WORD
|
||||||
|
DataType.ARRAY_F -> Syscall.ALL_FLOAT
|
||||||
|
else -> throw IllegalArgumentException("weird type")
|
||||||
|
}
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args[0], 0, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||||
|
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
|
||||||
|
if(resultRegister!=0)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val sourceDt = call.args.single().type
|
||||||
|
if(sourceDt!=DataType.UWORD) {
|
||||||
|
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||||
|
when (sourceDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
val andReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80)
|
||||||
|
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, labelSymbol = notNegativeLabel)
|
||||||
|
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||||
|
code += VmCodeLabel(notNegativeLabel)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val andReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000)
|
||||||
|
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, labelSymbol = notNegativeLabel)
|
||||||
|
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
|
||||||
|
code += VmCodeLabel(notNegativeLabel)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPop(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
|
||||||
|
code += assignRegisterTo(call.args.single(), reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPopw(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
|
||||||
|
code += assignRegisterTo(call.args.single(), reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPush(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPushw(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val reg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSwap(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val left = call.args[0]
|
||||||
|
val right = call.args[1]
|
||||||
|
val leftReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val rightReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(left, leftReg, -1)
|
||||||
|
code += exprGen.translateExpression(right, rightReg, -1)
|
||||||
|
code += assignRegisterTo(left, rightReg)
|
||||||
|
code += assignRegisterTo(right, leftReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||||
|
val sortSyscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS
|
||||||
|
DataType.ARRAY_F -> Syscall.REVERSE_FLOATS
|
||||||
|
else -> throw IllegalArgumentException("weird type to reverse")
|
||||||
|
}
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args[0], 0, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||||
|
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSort(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
|
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||||
|
val sortSyscall =
|
||||||
|
when(array.dt) {
|
||||||
|
DataType.ARRAY_UB -> Syscall.SORT_UBYTE
|
||||||
|
DataType.ARRAY_B -> Syscall.SORT_BYTE
|
||||||
|
DataType.ARRAY_UW -> Syscall.SORT_UWORD
|
||||||
|
DataType.ARRAY_W -> Syscall.SORT_WORD
|
||||||
|
DataType.STR -> Syscall.SORT_UBYTE
|
||||||
|
DataType.ARRAY_F -> throw java.lang.IllegalArgumentException("sorting a floating point array is not supported")
|
||||||
|
else -> throw IllegalArgumentException("weird type to sort")
|
||||||
|
}
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args[0], 0, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||||
|
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val msbReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args[0], msbReg, -1)
|
||||||
|
code += exprGen.translateExpression(call.args[1], resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPokeW(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isZero(call.args[1])) {
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.vmRegisters.nextFree()
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPoke(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isZero(call.args[1])) {
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.vmRegisters.nextFree()
|
||||||
|
if (call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||||
|
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(call.args[0] is PtNumber) {
|
||||||
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
|
||||||
|
} else {
|
||||||
|
val addressReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRnd(resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += VmCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRndw(resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += VmCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
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
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt())
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += 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.
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.MSIG, VmDataType.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 code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
|
val vmDt = codeGen.vmType(call.args[0].type)
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(opcode, vmDt, reg1=resultRegister)
|
||||||
|
code += assignRegisterTo(call.args[0], resultRegister)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignRegisterTo(target: PtExpression, register: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
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))
|
||||||
|
code += codeGen.translateNode(assignment)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
}
|
832
codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt
Normal file
832
codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt
Normal file
@ -0,0 +1,832 @@
|
|||||||
|
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 // integer registers 0,1,2 are reserved
|
||||||
|
private var firstFreeFloat: Int=0
|
||||||
|
|
||||||
|
fun peekNext() = firstFree
|
||||||
|
fun peekNextFloat() = firstFreeFloat
|
||||||
|
|
||||||
|
fun nextFree(): Int {
|
||||||
|
val result = firstFree
|
||||||
|
firstFree++
|
||||||
|
if(firstFree>65535)
|
||||||
|
throw AssemblyError("out of virtual registers (int)")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextFreeFloat(): Int {
|
||||||
|
val result = firstFreeFloat
|
||||||
|
firstFreeFloat++
|
||||||
|
if(firstFreeFloat>65535)
|
||||||
|
throw AssemblyError("out of virtual registers (fp)")
|
||||||
|
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)
|
||||||
|
private val assignmentGen = AssignmentGen(this, expressionEval)
|
||||||
|
internal val vmRegisters = VmRegisterPool()
|
||||||
|
|
||||||
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
|
val vmprog = AssemblyProgram(program.name, allocations)
|
||||||
|
|
||||||
|
if(!options.dontReinitGlobals) {
|
||||||
|
// collect global variables initializers
|
||||||
|
program.allBlocks().forEach {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += assignmentGen.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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal 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 -> assignmentGen.translate(node)
|
||||||
|
is PtNodeGroup -> translateGroup(node.children)
|
||||||
|
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
||||||
|
is PtFunctionCall -> expressionEval.translate(node, 0, 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 PtConditionalBranch -> translate(node)
|
||||||
|
is PtInlineAssembly -> VmCodeChunk(VmCodeInlineAsm(node.assembly))
|
||||||
|
is PtIncludeBinary -> VmCodeChunk(VmCodeInlineBinary(node.file, node.offset, node.length))
|
||||||
|
is PtAsmSub -> TODO("asmsub not yet supported on virtual machine target ${node.position}")
|
||||||
|
is PtAddressOf,
|
||||||
|
is 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("should not occur as separate statement node ${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(branch: PtConditionalBranch): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||||
|
code += when(branch.condition) {
|
||||||
|
BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
|
||||||
|
BranchCondition.VC,
|
||||||
|
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}")
|
||||||
|
}
|
||||||
|
code += translateNode(branch.trueScope)
|
||||||
|
if(branch.falseScope.children.isNotEmpty()) {
|
||||||
|
val endLabel = createLabelName()
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||||
|
code += VmCodeLabel(elseLabel)
|
||||||
|
code += translateNode(branch.falseScope)
|
||||||
|
code += VmCodeLabel(endLabel)
|
||||||
|
} else {
|
||||||
|
code += VmCodeLabel(elseLabel)
|
||||||
|
}
|
||||||
|
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, -1)
|
||||||
|
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, labelSymbol = skipLabel)
|
||||||
|
code += translateNode(choice.statements)
|
||||||
|
if(choice.statements.children.last() !is PtReturn)
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = 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, labelSymbol = matchLabel)
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||||
|
code += VmCodeLabel(matchLabel)
|
||||||
|
code += translateNode(choice.statements)
|
||||||
|
if(choice.statements.children.last() !is PtReturn)
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = 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 tmpReg = 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=tmpReg, reg2=indexReg, value = arrayAddress)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, value = loopvarAddress)
|
||||||
|
code += translateNode(forLoop.statements)
|
||||||
|
code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = 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
|
||||||
|
if(lengthBytes<256) {
|
||||||
|
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.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, value=arrayAddress)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, value = loopvarAddress)
|
||||||
|
code += translateNode(forLoop.statements)
|
||||||
|
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
|
||||||
|
code += VmCodeInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
|
||||||
|
} else if(lengthBytes==256) {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||||
|
code += VmCodeLabel(loopLabel)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, value=arrayAddress)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, value = loopvarAddress)
|
||||||
|
code += translateNode(forLoop.statements)
|
||||||
|
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
|
||||||
|
code += VmCodeInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("iterator length should never exceed 256")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, -1)
|
||||||
|
code += expressionEval.translateExpression(iterable.from, indexReg, -1)
|
||||||
|
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, labelSymbol=loopLabel)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
|
||||||
|
val loopLabel = createLabelName()
|
||||||
|
val loopvarAddress = allocations.get(loopvar.scopedName)
|
||||||
|
val indexReg = vmRegisters.nextFree()
|
||||||
|
val loopvarDt = vmType(loopvar.dt)
|
||||||
|
val iterable = forLoop.iterable as PtRange
|
||||||
|
val step = iterable.step.number.toInt()
|
||||||
|
val rangeStart = (iterable.from as PtNumber).number.toInt()
|
||||||
|
val rangeEndUntyped = (iterable.to as PtNumber).number.toInt() + step
|
||||||
|
if(step==0)
|
||||||
|
throw AssemblyError("step 0")
|
||||||
|
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
|
||||||
|
throw AssemblyError("empty range")
|
||||||
|
val rangeEndWrapped = if(loopvarDt==VmDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val endvalueReg: Int
|
||||||
|
if(rangeEndWrapped!=0) {
|
||||||
|
endvalueReg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
|
||||||
|
} else {
|
||||||
|
endvalueReg = -1 // not used
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
code += if(rangeEndWrapped==0) {
|
||||||
|
VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
|
||||||
|
} else {
|
||||||
|
VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = 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 = valueReg)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
|
||||||
|
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = 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 = 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 = operandReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun multiplyByConstFloat(fpReg: Int, factor: Float): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1f)
|
||||||
|
return code
|
||||||
|
if(factor==0f) {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
||||||
|
} else {
|
||||||
|
val factorReg = vmRegisters.nextFreeFloat()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||||
|
code += VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun multiplyByConstFloatInplace(address: Int, factor: Float): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1f)
|
||||||
|
return code
|
||||||
|
if(factor==0f) {
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = address)
|
||||||
|
} else {
|
||||||
|
val factorReg = vmRegisters.nextFreeFloat()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||||
|
code += VmCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = address)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
|
||||||
|
|
||||||
|
internal fun multiplyByConst(dt: VmDataType, reg: Int, factor: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1)
|
||||||
|
return code
|
||||||
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
|
if(pow2==1) {
|
||||||
|
// just shift 1 bit
|
||||||
|
code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg)
|
||||||
|
}
|
||||||
|
else if(pow2>=1) {
|
||||||
|
// just shift multiple bits
|
||||||
|
val pow2reg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
|
code += VmCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||||
|
} else {
|
||||||
|
if (factor == 0) {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val factorReg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||||
|
code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=factorReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun multiplyByConstInplace(dt: VmDataType, address: Int, factor: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1)
|
||||||
|
return code
|
||||||
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
|
if(pow2==1) {
|
||||||
|
// just shift 1 bit
|
||||||
|
code += VmCodeInstruction(Opcode.LSLM, dt, value = address)
|
||||||
|
}
|
||||||
|
else if(pow2>=1) {
|
||||||
|
// just shift multiple bits
|
||||||
|
val pow2reg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
|
code += VmCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=address)
|
||||||
|
} else {
|
||||||
|
if (factor == 0) {
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, dt, value=address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val factorReg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
|
||||||
|
code += VmCodeInstruction(Opcode.MULM, dt, reg1=factorReg, value = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun divideByConstFloat(fpReg: Int, factor: Float): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1f)
|
||||||
|
return code
|
||||||
|
if(factor==0f) {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
||||||
|
} else {
|
||||||
|
val factorReg = vmRegisters.nextFreeFloat()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||||
|
code += VmCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun divideByConstFloatInplace(address: Int, factor: Float): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1f)
|
||||||
|
return code
|
||||||
|
if(factor==0f) {
|
||||||
|
val maxvalueReg = vmRegisters.nextFreeFloat()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=address)
|
||||||
|
} else {
|
||||||
|
val factorReg = vmRegisters.nextFreeFloat()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||||
|
code += VmCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=address)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun divideByConst(dt: VmDataType, reg: Int, factor: Int, signed: Boolean): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1)
|
||||||
|
return code
|
||||||
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
|
if(pow2==1) {
|
||||||
|
// just shift 1 bit
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.ASR, dt, reg1=reg)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.LSR, dt, reg1=reg)
|
||||||
|
}
|
||||||
|
else if(pow2>=1) {
|
||||||
|
// just shift multiple bits
|
||||||
|
val pow2reg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
||||||
|
} else {
|
||||||
|
if (factor == 0) {
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val factorReg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVS, dt, reg1=reg, reg2=factorReg)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, reg2=factorReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun divideByConstInplace(dt: VmDataType, address: Int, factor: Int, signed: Boolean): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(factor==1)
|
||||||
|
return code
|
||||||
|
val pow2 = powersOfTwo.indexOf(factor)
|
||||||
|
if(pow2==1) {
|
||||||
|
// just shift 1 bit
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.ASRM, dt, value=address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.LSRM, dt, value=address)
|
||||||
|
}
|
||||||
|
else if(pow2>=1) {
|
||||||
|
// just shift multiple bits
|
||||||
|
val pow2reg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.ASRNM, dt, reg1=pow2reg, value=address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.LSRNM, dt, reg1=pow2reg, value=address)
|
||||||
|
} else {
|
||||||
|
if (factor == 0) {
|
||||||
|
val reg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val factorReg = vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVSM, dt, reg1=factorReg, value=address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIVM, dt, reg1=factorReg, value=address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(ifElse: PtIfElse): VmCodeChunk {
|
||||||
|
if(ifElse.condition.operator !in ComparisonOperators)
|
||||||
|
throw AssemblyError("if condition should only be a binary comparison expression")
|
||||||
|
|
||||||
|
val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||||
|
val vmDt = vmType(ifElse.condition.left.type)
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
|
||||||
|
fun translateNonZeroComparison(): VmCodeChunk {
|
||||||
|
val elseBranch = when(ifElse.condition.operator) {
|
||||||
|
"==" -> Opcode.BNE
|
||||||
|
"!=" -> Opcode.BEQ
|
||||||
|
"<" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||||
|
">" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||||
|
"<=" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||||
|
">=" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||||
|
else -> throw AssemblyError("invalid comparison operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
val leftReg = vmRegisters.nextFree()
|
||||||
|
val rightReg = vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
|
||||||
|
code += expressionEval.translateExpression(ifElse.condition.right, rightReg, -1)
|
||||||
|
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||||
|
// if and else parts
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
|
||||||
|
code += translateNode(ifElse.ifScope)
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||||
|
code += VmCodeLabel(elseLabel)
|
||||||
|
code += translateNode(ifElse.elseScope)
|
||||||
|
code += VmCodeLabel(afterIfLabel)
|
||||||
|
} else {
|
||||||
|
// only if part
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
|
||||||
|
code += translateNode(ifElse.ifScope)
|
||||||
|
code += VmCodeLabel(afterIfLabel)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateZeroComparison(): VmCodeChunk {
|
||||||
|
fun equalOrNotEqualZero(elseBranch: Opcode): VmCodeChunk {
|
||||||
|
val leftReg = vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
|
||||||
|
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||||
|
// if and else parts
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
|
||||||
|
code += translateNode(ifElse.ifScope)
|
||||||
|
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||||
|
code += VmCodeLabel(elseLabel)
|
||||||
|
code += translateNode(ifElse.elseScope)
|
||||||
|
code += VmCodeLabel(afterIfLabel)
|
||||||
|
} else {
|
||||||
|
// only if part
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
|
||||||
|
code += translateNode(ifElse.ifScope)
|
||||||
|
code += VmCodeLabel(afterIfLabel)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (ifElse.condition.operator) {
|
||||||
|
"==" -> {
|
||||||
|
// if X==0 ... so we just branch on left expr is Not-zero.
|
||||||
|
equalOrNotEqualZero(Opcode.BNZ)
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
// if X!=0 ... so we just branch on left expr is Zero.
|
||||||
|
equalOrNotEqualZero(Opcode.BZ)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// another comparison against 0, just use regular codegen for this.
|
||||||
|
translateNonZeroComparison()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(constValue(ifElse.condition.right)==0.0)
|
||||||
|
translateZeroComparison()
|
||||||
|
else
|
||||||
|
translateNonZeroComparison()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun translate(postIncrDecr: PtPostIncrDecr): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val operationMem: Opcode
|
||||||
|
val operationRegister: Opcode
|
||||||
|
when(postIncrDecr.operator) {
|
||||||
|
"++" -> {
|
||||||
|
operationMem = Opcode.INCM
|
||||||
|
operationRegister = Opcode.INC
|
||||||
|
}
|
||||||
|
"--" -> {
|
||||||
|
operationMem = Opcode.DECM
|
||||||
|
operationRegister = 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)
|
||||||
|
if(ident!=null) {
|
||||||
|
val address = allocations.get(ident.targetName)
|
||||||
|
code += VmCodeInstruction(operationMem, vmDt, value = address)
|
||||||
|
} else if(memory!=null) {
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
val address = (memory.address as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(operationMem, vmDt, value = address)
|
||||||
|
} else {
|
||||||
|
val incReg = vmRegisters.nextFree()
|
||||||
|
val addressReg = vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
|
||||||
|
code += VmCodeInstruction(operationRegister, vmDt, reg1 = incReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1 = incReg, 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 = constIntValue(array.index)
|
||||||
|
if(fixedIndex!=null) {
|
||||||
|
variableAddr += fixedIndex*itemsize
|
||||||
|
code += VmCodeInstruction(operationMem, vmDt, value=variableAddr)
|
||||||
|
} else {
|
||||||
|
val incReg = vmRegisters.nextFree()
|
||||||
|
val indexReg = vmRegisters.nextFree()
|
||||||
|
code += expressionEval.translateExpression(array.index, indexReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, value=variableAddr)
|
||||||
|
code += VmCodeInstruction(operationRegister, vmDt, reg1=incReg)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, value=variableAddr)
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("weird assigntarget")
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(repeat: PtRepeatLoop): VmCodeChunk {
|
||||||
|
when (constIntValue(repeat.count)) {
|
||||||
|
0 -> return VmCodeChunk()
|
||||||
|
1 -> return translateGroup(repeat.children)
|
||||||
|
256 -> {
|
||||||
|
// 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, -1)
|
||||||
|
val repeatLabel = createLabelName()
|
||||||
|
code += VmCodeLabel(repeatLabel)
|
||||||
|
code += translateNode(repeat.statements)
|
||||||
|
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
|
||||||
|
code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = 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, labelSymbol = listOf(jump.generatedLabel!!))
|
||||||
|
else if(jump.identifier!=null)
|
||||||
|
VmCodeInstruction(Opcode.JUMP, labelSymbol = 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(ret: PtReturn): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val value = ret.value
|
||||||
|
if(value!=null) {
|
||||||
|
// Call Convention: return value is always returned in r0 (or fr0 if float)
|
||||||
|
code += if(value.type==DataType.FLOAT)
|
||||||
|
expressionEval.translateExpression(value, -1, 0)
|
||||||
|
else
|
||||||
|
expressionEval.translateExpression(value, 0, -1)
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(Opcode.RETURN)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(sub: PtSub): VmCodeChunk {
|
||||||
|
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
|
||||||
|
DataType.FLOAT -> VmDataType.FLOAT
|
||||||
|
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)
|
||||||
|
|
||||||
|
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
|
||||||
|
|
||||||
|
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
||||||
|
}
|
||||||
|
|
846
codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt
Normal file
846
codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt
Normal file
@ -0,0 +1,846 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
|
import prog8.code.StSub
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.VmDataType
|
||||||
|
|
||||||
|
|
||||||
|
internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||||
|
fun translateExpression(expr: PtExpression, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
require(codeGen.vmRegisters.peekNext() > resultRegister)
|
||||||
|
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
|
||||||
|
when (expr) {
|
||||||
|
is PtMachineRegister -> {
|
||||||
|
if(resultRegister!=expr.register) {
|
||||||
|
val vmDt = codeGen.vmType(expr.type)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtNumber -> {
|
||||||
|
val vmDt = codeGen.vmType(expr.type)
|
||||||
|
code += if(vmDt==VmDataType.FLOAT)
|
||||||
|
VmCodeInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
|
||||||
|
}
|
||||||
|
is PtIdentifier -> {
|
||||||
|
val vmDt = codeGen.vmType(expr.type)
|
||||||
|
val mem = codeGen.allocations.get(expr.targetName)
|
||||||
|
code += if (expr.type in PassByValueDatatypes) {
|
||||||
|
if(vmDt==VmDataType.FLOAT)
|
||||||
|
VmCodeInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, value = mem)
|
||||||
|
else
|
||||||
|
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 vmDt = codeGen.vmType(expr.type)
|
||||||
|
val mem = codeGen.allocations.get(expr.identifier.targetName)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
|
||||||
|
}
|
||||||
|
is PtMemoryByte -> {
|
||||||
|
if(expr.address is PtNumber) {
|
||||||
|
val address = (expr.address as PtNumber).number.toInt()
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
|
||||||
|
} else {
|
||||||
|
val addressRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(expr.address, addressRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtTypeCast -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
|
is PtPrefix -> code += translate(expr, resultRegister)
|
||||||
|
is PtArrayIndexer -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
|
is PtBinaryExpression -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
|
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
||||||
|
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
|
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
|
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 {
|
||||||
|
val segments = pipe.segments
|
||||||
|
var valueDt = segments[0].type
|
||||||
|
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
|
||||||
|
|
||||||
|
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
|
||||||
|
return when (segment) {
|
||||||
|
is PtFunctionCall -> {
|
||||||
|
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
|
||||||
|
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
||||||
|
segWithArg.children.addAll(segment.args)
|
||||||
|
segWithArg
|
||||||
|
}
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.hasNoSideEffects, segment.type, segment.position)
|
||||||
|
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
||||||
|
segWithArg.children.addAll(segment.args)
|
||||||
|
segWithArg
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird segment type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += translateExpression(segments[0], valueReg, -1)
|
||||||
|
for (segment in segments.subList(1, segments.size-1)) {
|
||||||
|
val sourceReg = valueReg
|
||||||
|
val sourceDt = valueDt
|
||||||
|
if(segment.type!=valueDt) {
|
||||||
|
valueDt = segment.type
|
||||||
|
valueReg = codeGen.vmRegisters.nextFree()
|
||||||
|
}
|
||||||
|
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
|
||||||
|
code += translateExpression(segmentWithImplicitArgument, valueReg, -1)
|
||||||
|
}
|
||||||
|
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
|
||||||
|
code += translateExpression(segWithArg, resultRegister, -1)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
code += translateExpression(check.element, resultRegister, -1) // 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, resultFpRegister)
|
||||||
|
}
|
||||||
|
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, resultFpRegister)
|
||||||
|
}
|
||||||
|
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, resultFpRegister)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
|
||||||
|
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}")
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
|
||||||
|
val vmDt = codeGen.vmType(arrayIx.type)
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val idxReg = codeGen.vmRegisters.nextFree()
|
||||||
|
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
||||||
|
if(arrayIx.index is PtNumber) {
|
||||||
|
// optimized code when index is known - just calculate the memory address here
|
||||||
|
val memOffset = (arrayIx.index as PtNumber).number.toInt() * eltSize
|
||||||
|
if(vmDt==VmDataType.FLOAT)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, value=arrayLocation+memOffset)
|
||||||
|
else
|
||||||
|
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=arrayLocation+memOffset)
|
||||||
|
} else {
|
||||||
|
code += translateExpression(arrayIx.index, idxReg, -1)
|
||||||
|
if(eltSize>1)
|
||||||
|
code += codeGen.multiplyByConst(VmDataType.BYTE, idxReg, eltSize)
|
||||||
|
if(vmDt==VmDataType.FLOAT)
|
||||||
|
code += VmCodeInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, value = arrayLocation)
|
||||||
|
else
|
||||||
|
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, -1)
|
||||||
|
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=regMask)
|
||||||
|
}
|
||||||
|
"not" -> {
|
||||||
|
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(cast: PtTypeCast, predefinedResultRegister: Int, predefinedResultFpRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(cast.type==cast.value.type)
|
||||||
|
return code
|
||||||
|
val actualResultFpReg = if(predefinedResultFpRegister>=0) predefinedResultFpRegister else codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val actualResultReg = if(predefinedResultRegister>=0) predefinedResultRegister else codeGen.vmRegisters.nextFree()
|
||||||
|
if(cast.value.type==DataType.FLOAT) {
|
||||||
|
// a cast from float to integer, so evaluate the value into a float register first
|
||||||
|
code += translateExpression(cast.value, -1, actualResultFpReg)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
code += translateExpression(cast.value, actualResultReg, -1)
|
||||||
|
when(cast.type) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when(cast.value.type) {
|
||||||
|
DataType.BYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
|
||||||
|
DataType.FLOAT -> code += VmCodeInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
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 -> code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
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 = actualResultReg)
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
// ubyte -> uword: sign extend
|
||||||
|
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||||
|
}
|
||||||
|
DataType.WORD -> { }
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
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 = actualResultReg)
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
// byte -> word: sign extend
|
||||||
|
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||||
|
}
|
||||||
|
DataType.UWORD -> { }
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird cast value type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when(cast.value.type) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
code += VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird cast value type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird cast type")
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val vmDt = codeGen.vmType(binExpr.left.type)
|
||||||
|
val signed = binExpr.left.type in SignedDatatypes
|
||||||
|
return when(binExpr.operator) {
|
||||||
|
"+" -> operatorPlus(binExpr, vmDt, resultRegister, resultFpRegister)
|
||||||
|
"-" -> operatorMinus(binExpr, vmDt, resultRegister, resultFpRegister)
|
||||||
|
"*" -> operatorMultiply(binExpr, vmDt, resultRegister, resultFpRegister)
|
||||||
|
"/" -> operatorDivide(binExpr, vmDt, resultRegister, resultFpRegister, signed)
|
||||||
|
"%" -> operatorModulo(binExpr, vmDt, resultRegister)
|
||||||
|
"|", "or" -> operatorOr(binExpr, vmDt, resultRegister)
|
||||||
|
"&", "and" -> operatorAnd(binExpr, vmDt, resultRegister)
|
||||||
|
"^", "xor" -> operatorXor(binExpr, vmDt, resultRegister)
|
||||||
|
"<<" -> operatorShiftLeft(binExpr, vmDt, resultRegister)
|
||||||
|
">>" -> operatorShiftRight(binExpr, vmDt, resultRegister, signed)
|
||||||
|
"==" -> operatorEquals(binExpr, vmDt, resultRegister, false)
|
||||||
|
"!=" -> operatorEquals(binExpr, vmDt, resultRegister, true)
|
||||||
|
"<" -> operatorLessThan(binExpr, vmDt, resultRegister, signed, false)
|
||||||
|
">" -> operatorGreaterThan(binExpr, vmDt, resultRegister, signed, false)
|
||||||
|
"<=" -> operatorLessThan(binExpr, vmDt, resultRegister, signed, true)
|
||||||
|
">=" -> operatorGreaterThan(binExpr, vmDt, resultRegister, signed, true)
|
||||||
|
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorGreaterThan(
|
||||||
|
binExpr: PtBinaryExpression,
|
||||||
|
vmDt: VmDataType,
|
||||||
|
resultRegister: Int,
|
||||||
|
signed: Boolean,
|
||||||
|
greaterEquals: Boolean
|
||||||
|
): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||||
|
val ins = if (signed) {
|
||||||
|
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
||||||
|
} else {
|
||||||
|
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||||
|
} else {
|
||||||
|
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||||
|
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
|
||||||
|
comparisonCall.children.add(binExpr.left)
|
||||||
|
comparisonCall.children.add(binExpr.right)
|
||||||
|
code += translate(comparisonCall, resultRegister, -1)
|
||||||
|
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||||
|
code += if(greaterEquals)
|
||||||
|
VmCodeInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
val ins = if (signed) {
|
||||||
|
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
||||||
|
} else {
|
||||||
|
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorLessThan(
|
||||||
|
binExpr: PtBinaryExpression,
|
||||||
|
vmDt: VmDataType,
|
||||||
|
resultRegister: Int,
|
||||||
|
signed: Boolean,
|
||||||
|
lessEquals: Boolean
|
||||||
|
): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||||
|
val ins = if (signed) {
|
||||||
|
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
||||||
|
} else {
|
||||||
|
if (lessEquals) Opcode.SLE else Opcode.SLT
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||||
|
} else {
|
||||||
|
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||||
|
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
|
||||||
|
comparisonCall.children.add(binExpr.left)
|
||||||
|
comparisonCall.children.add(binExpr.right)
|
||||||
|
code += translate(comparisonCall, resultRegister, -1)
|
||||||
|
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||||
|
code += if(lessEquals)
|
||||||
|
VmCodeInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
val ins = if (signed) {
|
||||||
|
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
||||||
|
} else {
|
||||||
|
if (lessEquals) Opcode.SLE else Opcode.SLT
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorEquals(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, notEquals: Boolean): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||||
|
if (notEquals) {
|
||||||
|
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||||
|
} else {
|
||||||
|
val label = codeGen.createLabelName()
|
||||||
|
val valueReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
|
||||||
|
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
|
||||||
|
code += VmCodeLabel(label)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||||
|
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
|
||||||
|
comparisonCall.children.add(binExpr.left)
|
||||||
|
comparisonCall.children.add(binExpr.right)
|
||||||
|
code += translate(comparisonCall, resultRegister, -1)
|
||||||
|
if(notEquals) {
|
||||||
|
val maskReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=maskReg, value=1)
|
||||||
|
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=maskReg)
|
||||||
|
} else {
|
||||||
|
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
|
||||||
|
code += VmCodeInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorShiftRight(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, signed: Boolean): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isOne(binExpr.right)) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
val opc = if (signed) Opcode.ASR else Opcode.LSR
|
||||||
|
code += VmCodeInstruction(opc, vmDt, reg1 = resultRegister)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
|
||||||
|
code += VmCodeInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorShiftRightInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isOne(operand)) {
|
||||||
|
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
|
||||||
|
code += VmCodeInstruction(opc, vmDt, value=address)
|
||||||
|
} else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||||
|
code += VmCodeInstruction(opc, vmDt, reg1 = operandReg, value=address)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorShiftLeft(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isOne(binExpr.right)){
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorShiftLeftInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(codeGen.isOne(operand)){
|
||||||
|
code += VmCodeInstruction(Opcode.LSLM, vmDt, value=address)
|
||||||
|
} else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=address)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorXorInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = address)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorAndInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=address)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorOrInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = address)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
|
||||||
|
if(vmDt==VmDataType.FLOAT)
|
||||||
|
throw IllegalArgumentException("floating-point modulo not supported")
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorDivide(binExpr: PtBinaryExpression,
|
||||||
|
vmDt: VmDataType,
|
||||||
|
resultRegister: Int,
|
||||||
|
resultFpRegister: Int,
|
||||||
|
signed: Boolean): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
val factor = constFactorRight.number.toFloat()
|
||||||
|
code += codeGen.divideByConstFloat(resultFpRegister, factor)
|
||||||
|
} else {
|
||||||
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVS, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIV, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
val factor = constFactorRight.number.toInt()
|
||||||
|
code += codeGen.divideByConst(vmDt, resultRegister, factor, signed)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVS, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorDivideInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val constFactorRight = operand as? PtNumber
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
val factor = constFactorRight.number.toFloat()
|
||||||
|
code += codeGen.divideByConstFloatInplace(address, factor)
|
||||||
|
} else {
|
||||||
|
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(operand, -1, operandFpReg)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value=address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value=address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
val factor = constFactorRight.number.toInt()
|
||||||
|
code += codeGen.divideByConstInplace(vmDt, address, factor, signed)
|
||||||
|
} else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += if(signed)
|
||||||
|
VmCodeInstruction(Opcode.DIVSM, vmDt, reg1=operandReg, value = address)
|
||||||
|
else
|
||||||
|
VmCodeInstruction(Opcode.DIVM, vmDt, reg1=operandReg, value = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val constFactorLeft = binExpr.left as? PtNumber
|
||||||
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if(constFactorLeft!=null) {
|
||||||
|
code += translateExpression(binExpr.right, -1, resultFpRegister)
|
||||||
|
val factor = constFactorLeft.number.toFloat()
|
||||||
|
code += codeGen.multiplyByConstFloat(resultFpRegister, factor)
|
||||||
|
} else if(constFactorRight!=null) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
val factor = constFactorRight.number.toFloat()
|
||||||
|
code += codeGen.multiplyByConstFloat(resultFpRegister, factor)
|
||||||
|
} else {
|
||||||
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.MUL, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
|
||||||
|
code += translateExpression(binExpr.right, resultRegister, -1)
|
||||||
|
val factor = constFactorLeft.number.toInt()
|
||||||
|
code += codeGen.multiplyByConst(vmDt, resultRegister, factor)
|
||||||
|
} else if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
val factor = constFactorRight.number.toInt()
|
||||||
|
code += codeGen.multiplyByConst(vmDt, resultRegister, factor)
|
||||||
|
} else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorMultiplyInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
val constFactorRight = operand as? PtNumber
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if(constFactorRight!=null) {
|
||||||
|
val factor = constFactorRight.number.toFloat()
|
||||||
|
code += codeGen.multiplyByConstFloatInplace(address, factor)
|
||||||
|
} else {
|
||||||
|
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(operand, -1, operandFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||||
|
val factor = constFactorRight.number.toInt()
|
||||||
|
code += codeGen.multiplyByConstInplace(vmDt, address, factor)
|
||||||
|
} else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorMinus(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.SUB, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorMinusInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if((operand as? PtNumber)?.number==1.0) {
|
||||||
|
code += VmCodeInstruction(Opcode.DECM, vmDt, value=address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(operand, -1, operandFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((operand as? PtNumber)?.number==1.0) {
|
||||||
|
code += VmCodeInstruction(Opcode.DECM, vmDt, value=address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operatorPlus(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if((binExpr.left as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.right, -1, resultFpRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||||
|
}
|
||||||
|
else if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||||
|
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.ADD, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((binExpr.left as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.right, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
else if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||||
|
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=rightResultReg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun operatorPlusInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
|
||||||
|
val code = VmCodeChunk()
|
||||||
|
if(vmDt==VmDataType.FLOAT) {
|
||||||
|
if((operand as? PtNumber)?.number==1.0) {
|
||||||
|
code += VmCodeInstruction(Opcode.INCM, vmDt, value = address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(operand, -1, operandFpReg)
|
||||||
|
code += VmCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value=address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((operand as? PtNumber)?.number==1.0) {
|
||||||
|
code += VmCodeInstruction(Opcode.INCM, vmDt, value = address)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val operandReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(operand, operandReg, -1)
|
||||||
|
code += VmCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translate(fcall: PtFunctionCall, resultRegister: Int, resultFpRegister: 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 paramDt = codeGen.vmType(parameter.type)
|
||||||
|
if(codeGen.isZero(arg)) {
|
||||||
|
if (paramDt == VmDataType.FLOAT) {
|
||||||
|
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, paramDt, value = mem)
|
||||||
|
} else {
|
||||||
|
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREZM, paramDt, value = mem)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (paramDt == VmDataType.FLOAT) {
|
||||||
|
val argFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||||
|
code += translateExpression(arg, -1, argFpReg)
|
||||||
|
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, value = mem)
|
||||||
|
} else {
|
||||||
|
val argReg = codeGen.vmRegisters.nextFree()
|
||||||
|
code += translateExpression(arg, argReg, -1)
|
||||||
|
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
||||||
|
code += VmCodeInstruction(Opcode.STOREM, paramDt, reg1 = argReg, value = mem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code += VmCodeInstruction(Opcode.CALL, labelSymbol=fcall.functionName)
|
||||||
|
if(fcall.type==DataType.FLOAT) {
|
||||||
|
if (!fcall.void && resultFpRegister != 0) {
|
||||||
|
// Call convention: result value is in fr0, so put it in the required register instead.
|
||||||
|
code += VmCodeInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
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 $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]
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
|
@ -1,35 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
|
||||||
import prog8.ast.base.ByteDatatypes
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.PassByReferenceDatatypes
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import prog8.compiler.target.cbm.Petscii
|
|
||||||
import prog8.compilerinterface.ICompilationTarget
|
|
||||||
|
|
||||||
|
|
||||||
object C64Target: ICompilationTarget {
|
|
||||||
override val name = "c64"
|
|
||||||
override val machine = C64MachineDefinition
|
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
|
||||||
val coded = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
return coded.fold(
|
|
||||||
failure = { throw it },
|
|
||||||
success = { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
|
||||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypes -> 1
|
|
||||||
in WordDatatypes -> 2
|
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
|
||||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
|
||||||
else -> -9999999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
|
||||||
import prog8.ast.base.ByteDatatypes
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.PassByReferenceDatatypes
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.compiler.target.cbm.Petscii
|
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
|
||||||
import prog8.compilerinterface.ICompilationTarget
|
|
||||||
|
|
||||||
|
|
||||||
object Cx16Target: ICompilationTarget {
|
|
||||||
override val name = "cx16"
|
|
||||||
override val machine = CX16MachineDefinition
|
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
|
||||||
val coded= if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
return coded.fold(
|
|
||||||
failure = { throw it },
|
|
||||||
success = { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
|
||||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypes -> 1
|
|
||||||
in WordDatatypes -> 2
|
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
|
||||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
|
||||||
else -> -9999999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
|||||||
package prog8.compiler.target.c64
|
|
||||||
|
|
||||||
import prog8.compiler.target.cbm.viceMonListPostfix
|
|
||||||
import prog8.compilerinterface.*
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
object C64MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
// 5-byte cbm MFLPT format limitations:
|
|
||||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
|
||||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
|
||||||
override val FLOAT_MEM_SIZE = 5
|
|
||||||
override val POINTER_MEM_SIZE = 2
|
|
||||||
override val BASIC_LOAD_ADDRESS = 0x0801
|
|
||||||
override val RAW_LOAD_ADDRESS = 0xc000
|
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
|
||||||
override val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
|
||||||
override val ESTACK_HI = 0xcf00 // $ce00-$ceff inclusive
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
|
||||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
|
||||||
listOf("syslib")
|
|
||||||
else
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "${programNameWithPath}.$viceMonListPostfix",
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process
|
|
||||||
try {
|
|
||||||
process=processb.start()
|
|
||||||
} catch(x: IOException) {
|
|
||||||
continue // try the next emulator executable
|
|
||||||
}
|
|
||||||
process.waitFor()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isRegularRAMaddress(address: Int): Boolean = address<0xa000 || address in 0xc000..0xcfff
|
|
||||||
|
|
||||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = C64Zeropage(compilerOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
|
||||||
override val opcodeNames = 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")
|
|
||||||
|
|
||||||
|
|
||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG = 0x03 // temp storage for a register, must be B1+1
|
|
||||||
override val SCRATCH_W1 = 0xfb // temp storage 1 for a word $fb+$fc
|
|
||||||
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
|
||||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
|
||||||
|
|
||||||
if (options.zeropage == ZeropageType.FULL) {
|
|
||||||
free.addAll(0x04..0xf9)
|
|
||||||
free.add(0xff)
|
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
|
||||||
} else {
|
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
|
||||||
free.addAll(listOf(
|
|
||||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
|
||||||
0x16, 0x17, 0x18, 0x19, 0x1a,
|
|
||||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
|
||||||
0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
|
||||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
|
||||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
|
||||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
|
||||||
// 0x90-0xfa is 'kernal work storage area'
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
|
||||||
// remove the zeropage locations used for floating point operations from the free list
|
|
||||||
free.removeAll(listOf(
|
|
||||||
0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.zeropage!= ZeropageType.DONTUSE) {
|
|
||||||
// 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*
|
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
|
||||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
|
||||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9))
|
|
||||||
} else {
|
|
||||||
// don't use the zeropage at all
|
|
||||||
free.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short):
|
|
||||||
IMachineFloat {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
|
||||||
fun fromNumber(num: Number): Mflpt5 {
|
|
||||||
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
|
||||||
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
|
||||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
|
||||||
|
|
||||||
val flt = num.toDouble()
|
|
||||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
|
||||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
|
||||||
if (flt == 0.0)
|
|
||||||
return zero
|
|
||||||
|
|
||||||
val sign = if (flt < 0.0) 0x80L else 0x00L
|
|
||||||
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
|
||||||
var mantissa = flt.absoluteValue
|
|
||||||
|
|
||||||
// if mantissa is too large, shift right and adjust exponent
|
|
||||||
while (mantissa >= 0x100000000) {
|
|
||||||
mantissa /= 2.0
|
|
||||||
exponent++
|
|
||||||
}
|
|
||||||
// if mantissa is too small, shift left and adjust exponent
|
|
||||||
while (mantissa < 0x80000000) {
|
|
||||||
mantissa *= 2.0
|
|
||||||
exponent--
|
|
||||||
}
|
|
||||||
|
|
||||||
return when {
|
|
||||||
exponent < 0 -> zero // underflow, use zero instead
|
|
||||||
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
|
||||||
exponent == 0 -> zero
|
|
||||||
else -> {
|
|
||||||
val mantLong = mantissa.toLong()
|
|
||||||
Mflpt5(
|
|
||||||
exponent.toShort(),
|
|
||||||
(mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(),
|
|
||||||
(mantLong.and(0x00ff0000L) ushr 16).toShort(),
|
|
||||||
(mantLong.and(0x0000ff00L) ushr 8).toShort(),
|
|
||||||
(mantLong.and(0x000000ffL)).toShort())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toDouble(): Double {
|
|
||||||
if (this == zero) return 0.0
|
|
||||||
val exp = b0 - 128
|
|
||||||
val sign = (b1.toInt() and 0x80) > 0
|
|
||||||
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
|
||||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
|
||||||
return if (sign) -result else result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun makeFloatFillAsm(): String {
|
|
||||||
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
|
||||||
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
|
||||||
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
|
||||||
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
|
||||||
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
|
||||||
return "$b0, $b1, $b2, $b3, $b4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
package prog8.compiler.target.cbm
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.Ok
|
|
||||||
import com.github.michaelbull.result.Result
|
|
||||||
import com.github.michaelbull.result.mapError
|
|
||||||
import prog8.compilerinterface.CompilationOptions
|
|
||||||
import prog8.compilerinterface.IAssemblyProgram
|
|
||||||
import prog8.compilerinterface.OutputType
|
|
||||||
import prog8.compilerinterface.generatedLabelPrefix
|
|
||||||
import prog8.parser.SourceCode
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.Path
|
|
||||||
import kotlin.io.path.isRegularFile
|
|
||||||
|
|
||||||
|
|
||||||
internal const val viceMonListPostfix = "vice-mon-list"
|
|
||||||
|
|
||||||
class AssemblyProgram(
|
|
||||||
override val valid: Boolean,
|
|
||||||
override val name: String,
|
|
||||||
outputDir: Path,
|
|
||||||
private val compTarget: String) : IAssemblyProgram {
|
|
||||||
|
|
||||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
|
||||||
private val prgFile = outputDir.resolve("$name.prg")
|
|
||||||
private val binFile = outputDir.resolve("$name.bin")
|
|
||||||
private val viceMonListFile = outputDir.resolve("$name.$viceMonListPostfix")
|
|
||||||
|
|
||||||
override fun assemble(quiet: Boolean, options: CompilationOptions): Int {
|
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
|
||||||
|
|
||||||
if(quiet)
|
|
||||||
command.add("--quiet")
|
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
|
||||||
OutputType.PRG -> {
|
|
||||||
command.add("--cbm-prg")
|
|
||||||
println("\nCreating prg for target $compTarget.")
|
|
||||||
prgFile
|
|
||||||
}
|
|
||||||
OutputType.RAW -> {
|
|
||||||
command.add("--nostart")
|
|
||||||
println("\nCreating raw binary for target $compTarget.")
|
|
||||||
binFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
|
|
||||||
val proc = ProcessBuilder(command).inheritIO().start()
|
|
||||||
val result = proc.waitFor()
|
|
||||||
if (result == 0) {
|
|
||||||
removeGeneratedLabelsFromMonlist()
|
|
||||||
generateBreakpointList()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
|
||||||
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
|
||||||
val lines = viceMonListFile.toFile().readLines()
|
|
||||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
|
||||||
for (line in lines) {
|
|
||||||
if(pattern.matchEntire(line)==null)
|
|
||||||
it.write(line+"\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateBreakpointList() {
|
|
||||||
// builds list of breakpoints, appends to monitor list file
|
|
||||||
val breakpoints = mutableListOf<String>()
|
|
||||||
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
|
||||||
for (line in viceMonListFile.toFile().readLines()) {
|
|
||||||
val match = pattern.matchEntire(line)
|
|
||||||
if (match != null)
|
|
||||||
breakpoints.add("break \$" + match.groupValues[1])
|
|
||||||
}
|
|
||||||
val num = breakpoints.size
|
|
||||||
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
|
||||||
breakpoints.add(1, "; $num breakpoints have been defined")
|
|
||||||
breakpoints.add(2, "del")
|
|
||||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
|
||||||
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
|
||||||
return com.github.michaelbull.result.runCatching {
|
|
||||||
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").readText()
|
|
||||||
}.mapError { NoSuchFileException(File(filename)) }
|
|
||||||
} else {
|
|
||||||
val sib = Path(source.origin).resolveSibling(filename)
|
|
||||||
if (sib.isRegularFile())
|
|
||||||
Ok(SourceCode.File(sib).readText())
|
|
||||||
else
|
|
||||||
Ok(SourceCode.File(Path(filename)).readText())
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,242 +0,0 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen
|
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
|
||||||
|
|
||||||
|
|
||||||
fun optimizeAssembly(lines: MutableList<String>): Int {
|
|
||||||
|
|
||||||
var numberOfOptimizations = 0
|
|
||||||
|
|
||||||
var linesByFour = getLinesBy(lines, 4)
|
|
||||||
|
|
||||||
var mods = optimizeUselessStackByteWrites(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeIncDec(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeCmpSequence(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeStoreLoadSame(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods= optimizeJsrRts(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
var linesByFourteen = getLinesBy(lines, 14)
|
|
||||||
mods = optimizeSameAssignments(linesByFourteen)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFourteen = getLinesBy(lines, 14)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO more assembly optimizations
|
|
||||||
|
|
||||||
return numberOfOptimizations
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
|
||||||
|
|
||||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
|
||||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
|
||||||
if(modification.remove)
|
|
||||||
lines.removeAt(modification.lineIndex)
|
|
||||||
else
|
|
||||||
lines[modification.lineIndex] = modification.replacement!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
|
||||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
|
||||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
|
||||||
|
|
||||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// when statement (on bytes) generates a sequence of:
|
|
||||||
// lda $ce01,x
|
|
||||||
// cmp #$20
|
|
||||||
// beq check_prog8_s72choice_32
|
|
||||||
// lda $ce01,x
|
|
||||||
// cmp #$21
|
|
||||||
// beq check_prog8_s73choice_33
|
|
||||||
// the repeated lda can be removed
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for(lines in linesByFour) {
|
|
||||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
|
||||||
lines[1].value.trim().startsWith("cmp ") &&
|
|
||||||
lines[2].value.trim().startsWith("beq ") &&
|
|
||||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
|
||||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
|
||||||
// this is a lot harder for word values because the instruction sequence varies.
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for(lines in linesByFour) {
|
|
||||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
|
||||||
lines[1].value.trim()=="dex" &&
|
|
||||||
lines[2].value.trim()=="inx" &&
|
|
||||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
|
|
||||||
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
|
|
||||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
|
||||||
// @todo a better place to do this is in the Compiler instead and transform the Ast, or the AsmGen, and never even create the inefficient asm in the first place...
|
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for (pair in linesByFourteen) {
|
|
||||||
val first = pair[0].value.trimStart()
|
|
||||||
val second = pair[1].value.trimStart()
|
|
||||||
val third = pair[2].value.trimStart()
|
|
||||||
val fourth = pair[3].value.trimStart()
|
|
||||||
val fifth = pair[4].value.trimStart()
|
|
||||||
val sixth = pair[5].value.trimStart()
|
|
||||||
val seventh = pair[6].value.trimStart()
|
|
||||||
val eighth = pair[7].value.trimStart()
|
|
||||||
|
|
||||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
|
||||||
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
|
|
||||||
val firstvalue = first.substring(4)
|
|
||||||
val secondvalue = second.substring(4)
|
|
||||||
val thirdvalue = fifth.substring(4)
|
|
||||||
val fourthvalue = sixth.substring(4)
|
|
||||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
|
||||||
// lda/ldy sta/sty twice the isSameAs word --> remove second lda/ldy pair (fifth and sixth lines)
|
|
||||||
mods.add(Modification(pair[4].index, true, null))
|
|
||||||
mods.add(Modification(pair[5].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
|
|
||||||
val firstvalue = first.substring(4)
|
|
||||||
val secondvalue = third.substring(4)
|
|
||||||
if(firstvalue==secondvalue) {
|
|
||||||
// lda value / sta ? / lda isSameAs-value / sta ? -> remove second lda (third line)
|
|
||||||
mods.add(Modification(pair[2].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
|
||||||
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
|
||||||
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
|
||||||
|
|
||||||
val nineth = pair[8].value.trimStart()
|
|
||||||
val tenth = pair[9].value.trimStart()
|
|
||||||
val eleventh = pair[10].value.trimStart()
|
|
||||||
val twelveth = pair[11].value.trimStart()
|
|
||||||
val thirteenth = pair[12].value.trimStart()
|
|
||||||
val fourteenth = pair[13].value.trimStart()
|
|
||||||
|
|
||||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
|
||||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
|
||||||
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
|
||||||
|
|
||||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
|
||||||
// identical float init
|
|
||||||
mods.add(Modification(pair[7].index, true, null))
|
|
||||||
mods.add(Modification(pair[8].index, true, null))
|
|
||||||
mods.add(Modification(pair[9].index, true, null))
|
|
||||||
mods.add(Modification(pair[10].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
|
||||||
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM) but how does this code know?
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for (pair in linesByFour) {
|
|
||||||
val first = pair[0].value.trimStart()
|
|
||||||
val second = pair[1].value.trimStart()
|
|
||||||
|
|
||||||
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
|
||||||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
|
||||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
|
||||||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
|
||||||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
|
||||||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
|
|
||||||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
|
||||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
|
||||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
|
||||||
) {
|
|
||||||
val third = pair[2].value.trimStart()
|
|
||||||
if(!third.startsWith("b")) {
|
|
||||||
// no branch instruction follows, we can potentiall remove the load instruction
|
|
||||||
val firstLoc = first.substring(4).trimStart()
|
|
||||||
val secondLoc = second.substring(4).trimStart()
|
|
||||||
if (firstLoc == secondLoc) {
|
|
||||||
mods.add(Modification(pair[1].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for (pair in linesByFour) {
|
|
||||||
val first = pair[0].value
|
|
||||||
val second = pair[1].value
|
|
||||||
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
|
||||||
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
|
||||||
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
|
||||||
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)) {
|
|
||||||
mods.add(Modification(pair[0].index, true, null))
|
|
||||||
mods.add(Modification(pair[1].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// jsr Sub + rts -> jmp Sub
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
for (pair in linesByFour) {
|
|
||||||
val first = pair[0].value
|
|
||||||
val second = pair[1].value
|
|
||||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
|
||||||
mods += Modification(pair[0].index, false, pair[0].value.replace("jsr", "jmp"))
|
|
||||||
mods += Modification(pair[1].index, true, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user