mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
440
Commits
v11.2
...
v12.0-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d9fbd2ccc | ||
|
|
7efc709538 | ||
|
|
79419a98d0 | ||
|
|
1c77d5d5e7 | ||
|
|
c6854e22a3 | ||
|
|
83acc2f285 | ||
|
|
6de95f7a3b | ||
|
|
82839a2d82 | ||
|
|
e5fc9b3132 | ||
|
|
ced4c5944a | ||
|
|
d4c460072b | ||
|
|
0b9384b556 | ||
|
|
6c3277e3e3 | ||
|
|
d9ff1eb38a | ||
|
|
e178097735 | ||
|
|
a1ab8ed208 | ||
|
|
6ababbf8f4 | ||
|
|
79629befc1 | ||
|
|
8022c0772a | ||
|
|
8ad2b4638b | ||
|
|
3a0392df8a | ||
|
|
beb28b061d | ||
|
|
c39acc5031 | ||
|
|
f54a29c415 | ||
|
|
783b111059 | ||
|
|
fb286f8b54 | ||
|
|
5999110e3f | ||
|
|
52a757ea78 | ||
|
|
28df08eea8 | ||
|
|
79505308ba | ||
|
|
a7e9d8e14b | ||
|
|
08f3abe5bf | ||
|
|
3ef09d7d9a | ||
|
|
a9142b9ce5 | ||
|
|
13e6f64d3b | ||
|
|
4a1256c772 | ||
|
|
ff9bec90ec | ||
|
|
a6fee1e510 | ||
|
|
80538f101e | ||
|
|
aee53b14c7 | ||
|
|
5eb2fc8d86 | ||
|
|
98f91bbf88 | ||
|
|
b48b36ef18 | ||
|
|
221a093e5f | ||
|
|
2ca1820d4e | ||
|
|
d58737d5be | ||
|
|
b52cee3154 | ||
|
|
9a76941e10 | ||
|
|
0285a4cce1 | ||
|
|
5a3aa1bd25 | ||
|
|
0f79351de9 | ||
|
|
3cdb25ce8e | ||
|
|
b7193bd0c6 | ||
|
|
10ff6a0095 | ||
|
|
d30f58004e | ||
|
|
17bdb22e4f | ||
|
|
a68209be37 | ||
|
|
7b40eade44 | ||
|
|
8a717c74b9 | ||
|
|
aa72ded21e | ||
|
|
8e53c83844 | ||
|
|
e2a2db1256 | ||
|
|
2afd2d4dae | ||
|
|
fad17cc094 | ||
|
|
f93d957999 | ||
|
|
8db0344cee | ||
|
|
32e531f951 | ||
|
|
a4769702f9 | ||
|
|
cf19fb8df1 | ||
|
|
79b8bb5c9f | ||
|
|
fc5889ec0b | ||
|
|
369303c46d | ||
|
|
d65670cc7b | ||
|
|
f74eeaee0f | ||
|
|
826fb3e9c2 | ||
|
|
a3d7b8a899 | ||
|
|
0cc36ed6e4 | ||
|
|
976bd52972 | ||
|
|
4a8d5def84 | ||
|
|
2f60716082 | ||
|
|
729efb04e1 | ||
|
|
4ea8b4d445 | ||
|
|
e800c165f9 | ||
|
|
fd9bd23449 | ||
|
|
8880ed1393 | ||
|
|
f7fde070ca | ||
|
|
5ada80779d | ||
|
|
8972235a0e | ||
|
|
e56f533e38 | ||
|
|
324fb7dbf7 | ||
|
|
44285b9b5d | ||
|
|
a68f477d61 | ||
|
|
ae9f99448e | ||
|
|
7c0fb10197 | ||
|
|
9e85571a7b | ||
|
|
9e10c15e2e | ||
|
|
6bd7752bac | ||
|
|
83ec437e8a | ||
|
|
4a1d05dd46 | ||
|
|
aa324e355a | ||
|
|
5cb8bcead7 | ||
|
|
bbd06c0c99 | ||
|
|
651830ea82 | ||
|
|
c70146f1dc | ||
|
|
d4e83b28bb | ||
|
|
bc58a25765 | ||
|
|
38645022c9 | ||
|
|
647cd0fbe1 | ||
|
|
ea8935a346 | ||
|
|
7ea80babfc | ||
|
|
dee761a99e | ||
|
|
88ee7a8187 | ||
|
|
eb8b408b82 | ||
|
|
3d10882f57 | ||
|
|
1988496512 | ||
|
|
88b074c208 | ||
|
|
c4c5636a81 | ||
|
|
c39d570b72 | ||
|
|
4ccd7f9f3a | ||
|
|
1c9c5aeef7 | ||
|
|
23ad540aa5 | ||
|
|
08810c2749 | ||
|
|
a52966f327 | ||
|
|
624220e9a3 | ||
|
|
842b11ed9e | ||
|
|
82267b3f56 | ||
|
|
67fb45a55b | ||
|
|
11186f1dbe | ||
|
|
0116fac201 | ||
|
|
817f4f8e7c | ||
|
|
866313209b | ||
|
|
28e351daab | ||
|
|
893e16d814 | ||
|
|
33470c47fc | ||
|
|
63f7b87572 | ||
|
|
e2901cca1b | ||
|
|
ce8006992a | ||
|
|
0b5413ad83 | ||
|
|
dd7adde387 | ||
|
|
23058b51a1 | ||
|
|
2f90c53ad0 | ||
|
|
c3be7ab4b3 | ||
|
|
a9b8fbc6c6 | ||
|
|
720988ae72 | ||
|
|
b0981a5fae | ||
|
|
ea5deeefbd | ||
|
|
054c98da7c | ||
|
|
dc434d034a | ||
|
|
a4a1b563aa | ||
|
|
3f34b83e0d | ||
|
|
9c63ef39c7 | ||
|
|
9f6106452e | ||
|
|
f9fbfe30e3 | ||
|
|
9a9bf170c6 | ||
|
|
7dd64b4f13 | ||
|
|
b6c0bac96f | ||
|
|
8ede098154 | ||
|
|
2a4a3b786e | ||
|
|
b4e0a2019e | ||
|
|
e14c3f8b59 | ||
|
|
c81f76226d | ||
|
|
edc353cc24 | ||
|
|
dcce519c69 | ||
|
|
0a16dcafc0 | ||
|
|
54d41b7f6f | ||
|
|
0541b84d09 | ||
|
|
1b420f7fe7 | ||
|
|
6a9a82ff9d | ||
|
|
aa36e6b19f | ||
|
|
51cb6aad50 | ||
|
|
b5ce409592 | ||
|
|
2119817e4a | ||
|
|
1efdfe8ea1 | ||
|
|
67d4180825 | ||
|
|
be31e190d2 | ||
|
|
a68cf3c812 | ||
|
|
c2bf9024f8 | ||
|
|
bd72eaad4c | ||
|
|
b5d1575823 | ||
|
|
1d306e5cdc | ||
|
|
b137164fe6 | ||
|
|
67d4ad50e1 | ||
|
|
c71066af4c | ||
|
|
6f0a0981bd | ||
|
|
49a4d9ba37 | ||
|
|
fcdfa741b9 | ||
|
|
e3e395836d | ||
|
|
3bab177d50 | ||
|
|
12abafb917 | ||
|
|
8dc2e47507 | ||
|
|
0be90dedf2 | ||
|
|
daf7c3357c | ||
|
|
e6bab3ceeb | ||
|
|
59387b2ae8 | ||
|
|
e8795859c5 | ||
|
|
bebe60b687 | ||
|
|
ddceec364e | ||
|
|
f8f20440d3 | ||
|
|
f8722faa4e | ||
|
|
d067fa4b73 | ||
|
|
26fbbf48a4 | ||
|
|
d5cc414221 | ||
|
|
b5e51ab937 | ||
|
|
552e55c29f | ||
|
|
a228908c1a | ||
|
|
15fc3b6c04 | ||
|
|
0456badd02 | ||
|
|
d28f154f1c | ||
|
|
399cf5118d | ||
|
|
a87f2640d3 | ||
|
|
a90ef274d7 | ||
|
|
1c02179c5c | ||
|
|
77584493fd | ||
|
|
a36709e638 | ||
|
|
341778ba67 | ||
|
|
8d63cce749 | ||
|
|
ec50b5a007 | ||
|
|
8e7bbcdbe0 | ||
|
|
37ecdc47b3 | ||
|
|
112ca3cc53 | ||
|
|
33b3a1664c | ||
|
|
8a0c02e264 | ||
|
|
31d84c8921 | ||
|
|
34bedbeef1 | ||
|
|
3b1b0985c1 | ||
|
|
e40ace9dea | ||
|
|
4c0e6e2640 | ||
|
|
08b314c37d | ||
|
|
86da9d3c7e | ||
|
|
4e61e25c02 | ||
|
|
5097d52d99 | ||
|
|
368387e1a7 | ||
|
|
09d2185bb1 | ||
|
|
5c02e2bd71 | ||
|
|
fb01389b3d | ||
|
|
aaa81210ce | ||
|
|
51269257ea | ||
|
|
23a853db1e | ||
|
|
9da430ffeb | ||
|
|
cc063124cf | ||
|
|
3b37b89951 | ||
|
|
844b537d1e | ||
|
|
caf1d4a22a | ||
|
|
d8e244df99 | ||
|
|
548e421e27 | ||
|
|
322fa7ea69 | ||
|
|
db6c887795 | ||
|
|
cf7bea0985 | ||
|
|
61fe55168a | ||
|
|
25d7f8808f | ||
|
|
1c4999ec87 | ||
|
|
c726d3f937 | ||
|
|
f70341df1b | ||
|
|
f0b791452e | ||
|
|
adf5600a9b | ||
|
|
6d4ccc5feb | ||
|
|
5f3829d5cc | ||
|
|
770ebdcd4a | ||
|
|
96f690e749 | ||
|
|
eabdd3a8f3 | ||
|
|
50650b966b | ||
|
|
65e34d4989 | ||
|
|
05dad5ab5f | ||
|
|
1a69a2f1bc | ||
|
|
435faafaad | ||
|
|
686b32dc29 | ||
|
|
0e64a22910 | ||
|
|
4f0839f27e | ||
|
|
bb1953267d | ||
|
|
acc630972a | ||
|
|
6a33be3fd8 | ||
|
|
cd8aae4681 | ||
|
|
11456496bd | ||
|
|
f5fc4e345c | ||
|
|
86eef7039f | ||
|
|
f4b2264fcf | ||
|
|
9b36ae2277 | ||
|
|
913ab03963 | ||
|
|
38448e471c | ||
|
|
67231af623 | ||
|
|
e31ef6f06f | ||
|
|
09d188106a | ||
|
|
d8e2116481 | ||
|
|
435dfbb932 | ||
|
|
ba93966474 | ||
|
|
ea8d17cdb2 | ||
|
|
082265fb25 | ||
|
|
d138a7a567 | ||
|
|
ea27d732ab | ||
|
|
9e557ce8ac | ||
|
|
924e28e9b3 | ||
|
|
e5d9af75de | ||
|
|
31c1bf8bc5 | ||
|
|
37d4055036 | ||
|
|
78b1076110 | ||
|
|
0a3c748e41 | ||
|
|
ebf79ef9e2 | ||
|
|
60a73248cd | ||
|
|
abbb7d7ba3 | ||
|
|
59c378089e | ||
|
|
0b789b5f0b | ||
|
|
4382b96a9a | ||
|
|
246e4f35a6 | ||
|
|
99b9370178 | ||
|
|
506062c6b6 | ||
|
|
d634061cd9 | ||
|
|
8353c689ca | ||
|
|
d59d8ff1fe | ||
|
|
e98e6f70ac | ||
|
|
53e442d509 | ||
|
|
134352ed7c | ||
|
|
f7cbfdff06 | ||
|
|
b28ee0819f | ||
|
|
5de626aab8 | ||
|
|
7aad5d486e | ||
|
|
701f155951 | ||
|
|
8c324d7514 | ||
|
|
522958e0e9 | ||
|
|
97390db5f5 | ||
|
|
af920d1427 | ||
|
|
779ebc0537 | ||
|
|
38949b82c3 | ||
|
|
d11386ef26 | ||
|
|
0e0377d1f0 | ||
|
|
55e0dbab27 | ||
|
|
4dc82f2c83 | ||
|
|
1ba5587404 | ||
|
|
835c4b6da3 | ||
|
|
dbd955b61e | ||
|
|
d20e2fd88c | ||
|
|
e0dea89477 | ||
|
|
ccc6b56e35 | ||
|
|
6fc2902895 | ||
|
|
c96e4b40d4 | ||
|
|
37da3e2170 | ||
|
|
2661d3c489 | ||
|
|
b89bbb9281 | ||
|
|
696bf636ed | ||
|
|
40952a788a | ||
|
|
0162e7a0c1 | ||
|
|
6ce099f176 | ||
|
|
476a4bac8e | ||
|
|
63a410a6df | ||
|
|
cca27faa3b | ||
|
|
803e6bd81a | ||
|
|
88269628a2 | ||
|
|
b920d553a0 | ||
|
|
5e2d0d0dfc | ||
|
|
2ae3bd68eb | ||
|
|
9c183f27eb | ||
|
|
8046023e82 | ||
|
|
e328520588 | ||
|
|
7eb079050c | ||
|
|
2fdd5543b2 | ||
|
|
d04164c0a6 | ||
|
|
b047731f82 | ||
|
|
4d91f92a2e | ||
|
|
98505d27b1 | ||
|
|
cd63a58ad9 | ||
|
|
170f8dd092 | ||
|
|
619dcb6a84 | ||
|
|
99ae8ea52e | ||
|
|
dc031c30eb | ||
|
|
1e702439b7 | ||
|
|
8debc42381 | ||
|
|
532d719089 | ||
|
|
b40860aca4 | ||
|
|
2cbe6b5f7f | ||
|
|
d2cc7ccdfa | ||
|
|
2cb183c6d8 | ||
|
|
84026b105f | ||
|
|
a4d0589f10 | ||
|
|
e375f6afce | ||
|
|
5a7bc04816 | ||
|
|
bd1894580e | ||
|
|
9e694c0337 | ||
|
|
c82586db28 | ||
|
|
dd2d466350 | ||
|
|
830da8de0a | ||
|
|
4e5ee333c8 | ||
|
|
9df899eb63 | ||
|
|
ca7491a702 | ||
|
|
1a07129865 | ||
|
|
4fbd67ff99 | ||
|
|
5bc6c50f42 | ||
|
|
063de3801d | ||
|
|
ae65266a4a | ||
|
|
8ed2401e0b | ||
|
|
d2e8ee8269 | ||
|
|
1f996e3b8b | ||
|
|
7108b74105 | ||
|
|
801fe1b604 | ||
|
|
fb44c87597 | ||
|
|
6b9cdbd482 | ||
|
|
0ab98033b5 | ||
|
|
14a2b96609 | ||
|
|
f829b689db | ||
|
|
dfda8b7ed5 | ||
|
|
4388466451 | ||
|
|
5c2f509a52 | ||
|
|
59582f5210 | ||
|
|
e2a8bdbdfb | ||
|
|
0916b943da | ||
|
|
9c7ebc883c | ||
|
|
0ee42b9aa0 | ||
|
|
37b3868ca3 | ||
|
|
a6835ce3f0 | ||
|
|
69c96ad99b | ||
|
|
b72877d59d | ||
|
|
05eb15d4f7 | ||
|
|
f1fec37c79 | ||
|
|
73f6880ff8 | ||
|
|
8a53742f31 | ||
|
|
9be40e85ff | ||
|
|
61079c1eb7 | ||
|
|
1075ee8fc3 | ||
|
|
a28b265197 | ||
|
|
20e534c468 | ||
|
|
da7aa5dc49 | ||
|
|
8f2a43ca0a | ||
|
|
d0909d7810 | ||
|
|
1641999d20 | ||
|
|
e16452037c | ||
|
|
344d79684a | ||
|
|
573a1d9b7b | ||
|
|
25ab57580c | ||
|
|
a332e0e3d1 | ||
|
|
376f1cb139 | ||
|
|
90f80558d7 | ||
|
|
e281994898 | ||
|
|
29fac122e1 | ||
|
|
1dc412eb90 | ||
|
|
3770a4fe0c | ||
|
|
79cda544c8 | ||
|
|
f04b97d890 | ||
|
|
3e9b4ccc45 | ||
|
|
2c3d838dd8 | ||
|
|
7668a3c660 | ||
|
|
5dd45b714a | ||
|
|
8b08895d0f |
No files matched your search
@@ -7,10 +7,13 @@ build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
|
||||
+1575
-7
File diff suppressed because it is too large.
Load diff
Generated
+4
-4
@@ -7,13 +7,13 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.1" />
|
||||
<option name="languageVersion" value="2.1" />
|
||||
<option name="apiVersion" value="2.2" />
|
||||
<option name="languageVersion" value="2.2" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||
<option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
<option name="version" value="2.2.0" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+10
-10
@@ -1,23 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+2
-2
@@ -4,8 +4,8 @@
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
+7
-7
@@ -1,19 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
|
||||
Generated
+5
-1
@@ -12,6 +12,7 @@
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
@@ -22,7 +23,10 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -61,15 +61,18 @@ What does Prog8 provide?
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be configured to execute in ROM
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- high-level program optimizations
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
@@ -94,7 +97,7 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64 OS, Foenix F256, ...
|
||||
- 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)
|
||||
|
||||
|
||||
|
||||
+5
-1
@@ -1,9 +1,11 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.10"
|
||||
kotlin("jvm") version "2.2.20"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -18,6 +20,8 @@ allprojects {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
// languageVersion.set(KotlinVersion.KOTLIN_2_3)
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -7,7 +5,7 @@ plugins {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
package prog8.code
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// the automatically generated module where all string literals are interned to:
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given path,
|
||||
* where links are replaced by the actual directories,
|
||||
* and containing no redundant path elements.
|
||||
* If the path doesn't refer to an existing directory or file on the file system,
|
||||
* it is returned unchanged.
|
||||
*/
|
||||
fun Path.sanitize(): Path {
|
||||
return try {
|
||||
this.toRealPath().normalize()
|
||||
} catch (_: java.nio.file.NoSuchFileException) {
|
||||
this.absolute().normalize()
|
||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
||||
@@ -96,12 +96,13 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"prog8_lib_structalloc" to FSignature(true, BaseDataType.UWORD),
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
@@ -131,10 +132,15 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
|
||||
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
|
||||
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"rsave" to FSignature(false, null),
|
||||
|
||||
@@ -32,6 +32,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var ignoreFootguns: Boolean = false,
|
||||
var outputDir: Path = Path(""),
|
||||
var quiet: Boolean = false,
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
init {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.util.Objects
|
||||
import java.util.*
|
||||
|
||||
enum class BaseDataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
@@ -13,6 +13,9 @@ enum class BaseDataType {
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
@@ -26,6 +29,7 @@ enum class BaseDataType {
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
@@ -34,7 +38,8 @@ enum class BaseDataType {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
@@ -50,73 +55,163 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
interface ISubType {
|
||||
val scopedNameString: String
|
||||
fun memsize(sizer: IMemSizer): Int
|
||||
fun sameas(other: ISubType): Boolean
|
||||
fun getFieldType(name: String): DataType?
|
||||
}
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
when {
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
else -> {
|
||||
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
|
||||
"sub and subtype can't both be set"
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub
|
||||
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
fun forDt(dt: BaseDataType): DataType {
|
||||
if(dt.isStructInstance)
|
||||
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
|
||||
return simpletypes.getValue(dt)
|
||||
}
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
|
||||
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
|
||||
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
fun pointer(dt: DataType): DataType = if(dt.isBasic)
|
||||
DataType(BaseDataType.POINTER, dt.base, null)
|
||||
else
|
||||
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
|
||||
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
|
||||
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
|
||||
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
|
||||
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
|
||||
}
|
||||
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
if (isBasic)
|
||||
return pointer(base)
|
||||
if (isString)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
if (isPointer)
|
||||
return UWORD
|
||||
if (isArray) {
|
||||
if (msb || isSplitWordArray)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
val elementDt = elementType()
|
||||
require(elementDt.isBasic)
|
||||
return pointer(elementDt)
|
||||
}
|
||||
if (subType != null)
|
||||
return pointer(this)
|
||||
return UWORD
|
||||
}
|
||||
}
|
||||
|
||||
fun dereference(): DataType {
|
||||
require(isPointer || isUnsignedWord)
|
||||
return when {
|
||||
isUnsignedWord -> forDt(BaseDataType.UBYTE)
|
||||
sub!=null -> forDt(sub)
|
||||
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
|
||||
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
|
||||
else -> throw IllegalArgumentException("cannot dereference this pointer type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
@@ -137,6 +232,15 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
@@ -149,6 +253,30 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}"
|
||||
subType!=null -> "^^${subType!!.scopedNameString}"
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
when {
|
||||
sub!=null -> sub.name.lowercase()
|
||||
subType!=null -> subType!!.scopedNameString
|
||||
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}["
|
||||
subType!=null -> "^^${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
@@ -176,18 +304,37 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord || this == targetType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
|
||||
BaseDataType.ARRAY_POINTER -> false
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
// note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to
|
||||
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
|
||||
memsizer.memorySize(sub)
|
||||
} else if(subType!=null) {
|
||||
subType!!.memsize(memsizer)
|
||||
} else {
|
||||
memsizer.memorySize(base)
|
||||
}
|
||||
|
||||
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
@@ -203,22 +350,27 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||
val isPointer = base.isPointer
|
||||
val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
|
||||
val isPointerToWord = base.isPointer && sub?.isWord==true
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
@@ -269,10 +421,13 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
|
||||
@@ -27,7 +27,8 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val additionalAssemblerOptions: List<String>
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
@@ -35,7 +36,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
|
||||
@@ -13,4 +13,6 @@ interface IErrorReporter {
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ interface IMemSizer {
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
||||
return memorySize(DataType.UWORD, null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
|
||||
@@ -23,8 +23,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
|
||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||
abstract val SCRATCH_REG : UInt // temp storage for a register byte, 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
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word
|
||||
abstract val SCRATCH_PTR : UInt // temp storage for a pointer
|
||||
|
||||
|
||||
// the variables allocated into Zeropage.
|
||||
@@ -38,7 +39,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +73,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString || datatype.isArray -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
@@ -82,7 +84,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
if(position!=null)
|
||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||
else
|
||||
@@ -122,6 +124,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
datatype.isString -> VarAllocation(address, datatype, size)
|
||||
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||
datatype.isPointer -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
@@ -154,7 +157,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.sanitize
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.InvalidPathException
|
||||
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}]"
|
||||
@@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
||||
if(SourceCode.isLibraryResource(file))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
val path = Path(file).sanitize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(_: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.sanitize
|
||||
import java.nio.file.Path
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// Resource caching "filesystem".
|
||||
@@ -22,7 +22,7 @@ object ImportFileSystem {
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||
val normalized = path.absolute().normalize()
|
||||
val normalized = path.sanitize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
@@ -48,7 +48,7 @@ object ImportFileSystem {
|
||||
val cached = cache[position.file]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).absolute().normalize()
|
||||
val path = Path(position.file).sanitize()
|
||||
val cached2 = cache[path.toString()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.sanitize
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
@@ -59,7 +60,7 @@ sealed class SourceCode {
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
||||
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
|
||||
@@ -6,12 +6,16 @@ import prog8.code.target.zp.C128Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
@@ -27,10 +31,10 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
override val BSSHIGHRAM_START = 0u // TODO address?
|
||||
override val BSSHIGHRAM_END = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_START = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_END = 0u // TODO address?
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
@@ -48,17 +52,21 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
if(!quiet)
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
@@ -7,12 +7,16 @@ import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c64"
|
||||
@@ -52,18 +56,22 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
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...")
|
||||
if(!quiet)
|
||||
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 processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
|
||||
@@ -24,19 +24,21 @@ class ConfigFileTarget(
|
||||
override val BSSHIGHRAM_END: UInt,
|
||||
override val BSSGOLDENRAM_START: UInt,
|
||||
override val BSSGOLDENRAM_END: UInt,
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
override val additionalAssemblerOptions: List<String>,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
val zpScratchW1: UInt,
|
||||
val zpScratchW2: UInt,
|
||||
val zpScratchPtr: UInt,
|
||||
val virtualregistersStart: UInt,
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -108,7 +110,8 @@ class ConfigFileTarget(
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
return ConfigFileTarget(
|
||||
configfile.nameWithoutExtension,
|
||||
@@ -121,14 +124,16 @@ class ConfigFileTarget(
|
||||
props.getInteger("bss_highram_end"),
|
||||
props.getInteger("bss_goldenram_start"),
|
||||
props.getInteger("bss_goldenram_end"),
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
props.getInteger("zp_scratch_w1"),
|
||||
props.getInteger("zp_scratch_w2"),
|
||||
props.getInteger("zp_scratch_ptr"),
|
||||
props.getInteger("virtual_registers"),
|
||||
zpFullsafe,
|
||||
zpKernalsafe,
|
||||
@@ -148,7 +153,7 @@ class ConfigFileTarget(
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||
}
|
||||
|
||||
@@ -156,7 +161,7 @@ class ConfigFileTarget(
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = ConfigurableZeropage(
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
|
||||
virtualregistersStart,
|
||||
zpBasicsafe,
|
||||
zpKernalsafe,
|
||||
|
||||
@@ -6,12 +6,16 @@ import prog8.code.target.zp.CX16Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
@@ -48,7 +52,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
@@ -67,9 +71,13 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
}
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
|
||||
@@ -18,7 +18,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
||||
@@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
@@ -18,14 +20,16 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isStructInstance -> dt.subType!!.memsize(this)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,16 @@ import prog8.code.target.zp.PETZeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
@@ -47,17 +51,21 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting PET emulator...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
@@ -3,16 +3,21 @@ package prog8.code.target
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
@@ -21,8 +26,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
@@ -39,7 +44,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||
val parts = hexStr.chunked(2).map { "$$it" }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
@@ -63,20 +68,17 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(!quiet)
|
||||
println("\nStarting Virtual Machine...")
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
}
|
||||
val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText(), quiet)
|
||||
else
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
@@ -85,37 +87,12 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String)
|
||||
fun runProgram(irSource: String, quiet: Boolean)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
@@ -127,4 +104,6 @@ private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_PTR: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
}
|
||||
@@ -26,7 +26,7 @@ object AtasciiEncoding {
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
@@ -62,7 +62,7 @@ object AtasciiEncoding {
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
@@ -80,7 +80,7 @@ object AtasciiEncoding {
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
@@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
|
||||
@@ -285,6 +285,7 @@ object C64osEncoding {
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
||||
@@ -5,19 +5,19 @@ import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
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)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
@@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
|
||||
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)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
||||
object KatakanaEncoding {
|
||||
val charset: Charset = Charset.forName("JIS_X0201")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
@@ -112,9 +113,14 @@ object KatakanaEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
@@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
||||
@@ -14,14 +14,9 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x75u // temp storage for a register byte, 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
|
||||
override val SCRATCH_PTR = 0x0bu // temp storage for a pointer $0b+$0c
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
|
||||
@@ -9,6 +9,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register byte, 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
|
||||
override val SCRATCH_PTR = 0x9bu // temp storage for a pointer $9b+$9c
|
||||
|
||||
|
||||
init {
|
||||
@@ -21,7 +22,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
@@ -85,12 +85,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
free.remove((4+reg*2).toUInt())
|
||||
free.remove((5+reg*2).toUInt())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, 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
|
||||
override val SCRATCH_PTR = 0x22u // temp storage for a pointer $22+$23
|
||||
|
||||
|
||||
init {
|
||||
@@ -57,12 +58,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class ConfigurableZeropage(
|
||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||
override val SCRATCH_PTR: UInt, // temp storage for a pointer
|
||||
|
||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||
basicsafe: List<UIntRange>,
|
||||
kernalsafe: List<UIntRange>,
|
||||
@@ -16,7 +21,7 @@ class ConfigurableZeropage(
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats")
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
@@ -27,7 +32,7 @@ class ConfigurableZeropage(
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe")
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
@@ -46,12 +51,12 @@ class ConfigurableZeropage(
|
||||
for(reg in 0..15) {
|
||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||
if(address<=0xffu) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,9 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
override val SCRATCH_PTR = 0xb1u // temp storage for a pointer $b1+$b2
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
@@ -38,7 +35,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
free.addAll(0xb1u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -9,12 +7,10 @@ dependencies {
|
||||
implementation(project(":simpleAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -73,6 +73,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
@@ -408,7 +415,7 @@ private fun optimizeStoreLoadSame(
|
||||
// a branch instruction follows, we can only remove the load instruction if
|
||||
// another load instruction of the same register precedes the store instruction
|
||||
// (otherwise wrong cpu flags are used)
|
||||
val loadinstruction = second.substring(0, 3)
|
||||
val loadinstruction = second.take(3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
@@ -446,20 +453,31 @@ private fun optimizeStoreLoadSame(
|
||||
|
||||
|
||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ")
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
|
||||
) {
|
||||
val firstVal = first.substring(4).trimStart()
|
||||
val thirdVal = third.substring(4).trimStart()
|
||||
if (firstVal == thirdVal)
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||
|
||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||
// try to get the constant value address, could return null if it's a symbol instead
|
||||
@@ -508,9 +526,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// jmp Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -520,7 +540,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
@@ -563,6 +586,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only remove bra followed by jmp or jmp followed by bra
|
||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -680,25 +712,36 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
optimize('x', lines)
|
||||
optimize('y', lines)
|
||||
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
val third = lines[3].value.trimStart()
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
|
||||
// phy + ldy + pla -> tya + ldy
|
||||
// phx + ldx + pla -> txa + ldx
|
||||
// pha + lda + pla -> nop
|
||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
// pha + tya + tay + pla -> nop
|
||||
// pha + txa + tax + pla -> nop
|
||||
when (first) {
|
||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " tya"))
|
||||
}
|
||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " txa"))
|
||||
}
|
||||
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
"pha" if ((second=="tya" && third=="tay") || (second=="txa" && third=="tax")) && fourth=="pla" -> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +749,6 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
return mods
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -751,3 +793,120 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
/*
|
||||
; FIRST SEQUYENCE: P8ZP_SCRATCH_PTR += AY :
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
pha
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
tay
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
|
||||
->
|
||||
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
sta P8ZP_SCRATCH_PTR+1
|
||||
|
||||
|
||||
also SECOND SEQUENCE:
|
||||
|
||||
ldx VALUE/ ldy VALUE
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
txa / tya
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
lda VALUE
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
|
||||
|
||||
also THIRD SEQUENCE:
|
||||
|
||||
ldx VALUE
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
txa
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
lda VALUE
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
*/
|
||||
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 eight = lines[7].value.trimStart()
|
||||
val ninth = lines[8].value.trimStart()
|
||||
|
||||
// FIRST SEQUENCE
|
||||
if(first=="clc" && second.startsWith("adc") && third=="pha" && fourth=="tya" &&
|
||||
fifth.startsWith("adc") && sixth=="tay" && seventh=="pla" && eight.startsWith("sta") && ninth.startsWith("sty")) {
|
||||
val var2 = second.substring(4)
|
||||
val var5 = fifth.substring(4).substringBefore('+')
|
||||
val var8 = eight.substring(4)
|
||||
val var9 = ninth.substring(4).substringBefore('+')
|
||||
if(var2==var5 && var2==var8 && var2==var9) {
|
||||
if(fifth.endsWith("$var5+1") && ninth.endsWith("$var9+1")) {
|
||||
mods.add(Modification(lines[2].index, false, " sta $var2"))
|
||||
mods.add(Modification(lines[5].index, false, " sta $var2+1"))
|
||||
mods.add(Modification(lines[6].index, true, null))
|
||||
mods.add(Modification(lines[7].index, true, null))
|
||||
mods.add(Modification(lines[8].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SECOND SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("sta ") &&
|
||||
third=="txa" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
if(first.startsWith("ldy ") && second.startsWith("sta ") &&
|
||||
third=="tya" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",y" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
// THIRD SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("ldy ") && third.startsWith("sta ") &&
|
||||
fourth=="txa" && fifth=="iny" && sixth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in third) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[3].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return mods
|
||||
}
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.OutputType
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.PETTarget
|
||||
@@ -25,6 +28,14 @@ internal class AssemblyProgram(
|
||||
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
fun addRemainingOptions(command: MutableList<String>, program: Path, assembly: Path): List<String> {
|
||||
if(options.compTarget.additionalAssemblerOptions.isNotEmpty())
|
||||
command.addAll(options.compTarget.additionalAssemblerOptions)
|
||||
|
||||
command.addAll(listOf("--output", program.toString(), assembly.toString()))
|
||||
return command
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.PRG -> {
|
||||
// CBM machines .prg generation.
|
||||
@@ -44,9 +55,9 @@ internal class AssemblyProgram(
|
||||
command.add("--list=$listFile")
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
// Atari800XL .xex generation.
|
||||
@@ -65,9 +76,9 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
// Neo6502/headerless raw program generation.
|
||||
@@ -85,9 +96,9 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.LIBRARY -> {
|
||||
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
||||
@@ -107,23 +118,24 @@ internal class AssemblyProgram(
|
||||
command.add("--list=$listFile")
|
||||
|
||||
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||
command.add("--cbm-prg")
|
||||
} else {
|
||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
}
|
||||
}
|
||||
|
||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||
|
||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
val proc = ProcessBuilder(assemblerCommand)
|
||||
if(!options.quiet)
|
||||
proc.inheritIO()
|
||||
val process = proc.start()
|
||||
val result = process.waitFor()
|
||||
if (result == 0) {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
@@ -149,7 +161,7 @@ internal class AssemblyProgram(
|
||||
for (line in viceMonListFile.toFile().readLines()) {
|
||||
val match = pattern.matchEntire(line)
|
||||
if (match != null)
|
||||
breakpoints.add("break \$" + match.groupValues[1])
|
||||
breakpoints.add("break $" + match.groupValues[1])
|
||||
}
|
||||
val num = breakpoints.size
|
||||
breakpoints.add(0, "; breakpoint list now follows")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -46,6 +47,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peekbool" -> funcPeekBool(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
@@ -53,19 +55,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
memread.add(fcall.args[0])
|
||||
memread.parent = fcall
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"pokebool" -> funcPokeBool(fcall)
|
||||
"rsave" -> funcRsave()
|
||||
"rrestore" -> funcRrestore()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
||||
@@ -107,8 +111,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
||||
}
|
||||
@@ -120,7 +124,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -196,7 +200,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
|
||||
asmgen.out("""
|
||||
; push a return address so the jmp becomes indirect jsr
|
||||
lda #>((+)-1)
|
||||
@@ -227,6 +231,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -236,7 +243,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
jsr $jsrfar
|
||||
+ .word 0
|
||||
+ .byte 0""")
|
||||
asmgen.romableWarning("self-modifying code for jsrfar", fcall.position) // TODO
|
||||
}
|
||||
|
||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||
@@ -284,6 +290,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar2 (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -293,7 +302,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
jsr $jsrfar
|
||||
+ .word 0
|
||||
+ .byte 0""")
|
||||
asmgen.romableWarning("self-modifying code for jsrfar", fcall.position) // TODO
|
||||
}
|
||||
|
||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||
@@ -376,16 +384,32 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.position)
|
||||
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val slabname = SymbolTable.labelnameForStructInstance(fcall)
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(PtIdentifier(slabname, fcall.type, fcall.position))
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type.base) {
|
||||
@@ -411,8 +435,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -434,8 +461,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
@@ -461,35 +490,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
asmgen.romableWarning("self-modifying code (ror)", fcall.position) // TODO
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff ; modified""")
|
||||
asmgen.romableWarning("self-modifying code (ror)", fcall.position) // TODO
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | ror a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("ror ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -506,7 +526,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
@@ -529,8 +551,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@@ -553,9 +577,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||
else
|
||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||
}
|
||||
@@ -579,35 +605,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
asmgen.romableWarning("self-modifying code (rol)", fcall.position) // TODO
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff ; modified""")
|
||||
asmgen.romableWarning("self-modifying code (rol)", fcall.position) // TODO
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | rol a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("rol ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,7 +640,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
@@ -646,14 +664,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when(fcall.args[0]) {
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable
|
||||
val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
|
||||
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
|
||||
indexer.children[0].parent = indexer
|
||||
elementSize = 1
|
||||
@@ -688,7 +706,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
multipliedIndex.parent=indexer
|
||||
}
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
|
||||
}
|
||||
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||
}
|
||||
@@ -696,7 +714,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(fcall.args[1].asConstInteger() == 0) {
|
||||
assignAsmGen.assignConstantByte(target, 0)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
@@ -759,8 +777,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignConstFloatToPointerAY(number)
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
@@ -774,6 +792,52 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPokeBool(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" sta $addr")
|
||||
return
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, varname, 0u, false, false)
|
||||
return
|
||||
}
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" sta ($varname),y")
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> { /* fall through */ }
|
||||
}
|
||||
|
||||
// fall through method:
|
||||
if(fcall.args[1].isSimple()) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
} else {
|
||||
asmgen.pushCpuStack(BaseDataType.UBYTE, fcall.args[1])
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W1", 0u, false, false)
|
||||
}
|
||||
|
||||
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
@@ -787,20 +851,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
sta ($varname)
|
||||
txa
|
||||
ldy #1
|
||||
sta ($varname),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
}
|
||||
asmgen.storeIndirectWordReg(RegisterOrPair.AX, varname, 0u)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -835,7 +886,46 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), register=resultRegister, position=fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeekBool(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peek")
|
||||
}
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" lda $addr")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectByte(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.out(" lda ($varname),y")
|
||||
} else fallback()
|
||||
}
|
||||
else -> fallback()
|
||||
}
|
||||
|
||||
when(resultRegister ?: RegisterOrPair.A) {
|
||||
RegisterOrPair.A -> {}
|
||||
RegisterOrPair.X -> asmgen.out(" tax")
|
||||
RegisterOrPair.Y -> asmgen.out(" tay")
|
||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().lowercase()}")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,25 +941,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($varname),y
|
||||
tay
|
||||
lda ($varname)""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($varname),y
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
} else fallback()
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectWord(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
@@ -1132,7 +1207,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the msb byte out of the word array
|
||||
if(arg.splitWords) {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1149,7 +1226,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1231,7 +1310,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the lsb byte out of the word array
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1296,7 +1378,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||
return when (value) {
|
||||
is PtIdentifier -> {
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1309,11 +1391,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
}
|
||||
}
|
||||
@@ -1330,7 +1412,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1348,7 +1430,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
|
||||
@@ -33,228 +33,351 @@ internal class ForLoopsAsmGen(
|
||||
}
|
||||
|
||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val stepsize=range.step.asConstInteger()!!
|
||||
|
||||
if(stepsize < -1) {
|
||||
if(range.step.asConstInteger()!! < -1) {
|
||||
val limit = range.to.asConstInteger()
|
||||
if(limit==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")
|
||||
}
|
||||
|
||||
when {
|
||||
iterableDt.isByteArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
when (stepsize) {
|
||||
-1 if range.to.asConstInteger()==0 -> {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
}
|
||||
-1 if range.to.asConstInteger()==1 -> {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
}
|
||||
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step 1 or -1
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
}
|
||||
else if (stepsize==1 || stepsize==-1) {
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
}
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// use self-modifying code to store the loop end comparison value
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1) {
|
||||
// words, step 1 or -1
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step >= 2 or <= -2
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
|
||||
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1)
|
||||
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else if (stepsize > 0)
|
||||
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else
|
||||
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// words range, step 1 or -1
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
asmgen.out(" sty $toValueVar+1")
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
cmp $toValueVar+1
|
||||
bne +
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bne +
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
beq $endLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize > 0) {
|
||||
// (u)words, step >= 2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step >= 2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -270,9 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
} else {
|
||||
asmgen.out("""
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -288,21 +410,23 @@ $modifiedLabel lda #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
// (u)words, step <= -2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step <= -2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
@@ -319,13 +443,6 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
@@ -337,23 +454,23 @@ $endLabel""")
|
||||
sta P8ZP_SCRATCH_W2 ; to
|
||||
sty P8ZP_SCRATCH_W2+1 ; to
|
||||
lda $fromVar
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
lda $fromVar+1
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
@@ -369,11 +486,11 @@ $endLabel""")
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
@@ -383,39 +500,56 @@ $endLabel""")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.romableWarning("self-modifying code (forloop over iterable)", stmt.position) // TODO
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
inc $loopLabel+2
|
||||
bne $loopLabel
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$indexVar .byte 0
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -430,20 +564,25 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -453,7 +592,7 @@ $loopLabel sty $indexVar
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -468,21 +607,25 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -492,16 +635,16 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127) {
|
||||
if(numElements<=127u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -509,15 +652,17 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||
// Just load them in whatever the register spec says.
|
||||
return when (params.size) {
|
||||
1 -> params[0].type.isIntegerOrBool && params[0].register == null
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||
else -> false
|
||||
}
|
||||
@@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||
if(symbol.type == StNodeType.LABEL) {
|
||||
require(call.void)
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
if(bank==null) {
|
||||
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||
if(varbank!=null) {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
|
||||
|
||||
// self-modifying code: set jsrfar bank argument
|
||||
when(asmgen.options.compTarget.name) {
|
||||
"cx16" -> {
|
||||
// JSRFAR can jump to a banked RAM address as well!
|
||||
@@ -72,7 +76,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||
+ .byte 0 ; modified"""
|
||||
)
|
||||
asmgen.romableWarning("self-modifying code for cx16 banked jsr", call.position) // TODO
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out("""
|
||||
@@ -86,7 +89,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||
+ .byte 0 ; modified"""
|
||||
)
|
||||
asmgen.romableWarning("self-modifying code for c64 banked jsr", call.position) // TODO
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out("""
|
||||
@@ -100,7 +102,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||
+ .byte 0 ; modified"""
|
||||
)
|
||||
asmgen.romableWarning("self-modifying code for c128 banked jsr", call.position) // TODO
|
||||
}
|
||||
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
|
||||
}
|
||||
@@ -138,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(optimizeIntArgsViaCpuRegisters(parameters)) {
|
||||
// Note that if the args fit into cpu registers, we don't concern ourselves here
|
||||
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
|
||||
// That is now up to the subroutine itself.
|
||||
useCpuRegistersForArgs(call.args, sub)
|
||||
} else {
|
||||
// arguments via variables
|
||||
val paramValues = sub.parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||
val paramValues = parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
|
||||
if (normalParams.isNotEmpty()) {
|
||||
for (p in normalParams)
|
||||
argumentViaVariable(sub, p.first, p.second)
|
||||
@@ -165,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
|
||||
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
when(params.size) {
|
||||
1 -> {
|
||||
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||
@@ -233,9 +235,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val param = sub.parameters[it]
|
||||
val arg = call.args[it]
|
||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
||||
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
else if(registersUsed.any {it.statusflag!=null}) {
|
||||
else if(registersUsed.any { r-> r.statusflag!=null }) {
|
||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||
}
|
||||
else {
|
||||
@@ -325,20 +327,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
register!!
|
||||
if(requiredDt.largerSizeThan(value.type)) {
|
||||
// we need to sign extend the source, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||
} else {
|
||||
val scope = value.definingISub()
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
if(parameter.value.type.isByte && register.isWord())
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||
else {
|
||||
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
|
||||
}
|
||||
val src = if(value.type.isPassByRef) {
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
|
||||
@@ -6,11 +6,13 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||
import prog8.codegen.cpu6502.assignment.PointerAssignmentsGen
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
|
||||
internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private val st: SymbolTable,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val pointergen: PointerAssignmentsGen,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val errors: IErrorReporter) {
|
||||
|
||||
@@ -38,13 +40,12 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testBitSet, variable, bitmask) = useBIT
|
||||
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
||||
return
|
||||
}
|
||||
|
||||
val rightDt = compareCond.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -64,6 +65,16 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val dereference = stmt.condition as? PtPointerDeref
|
||||
if(dereference!=null) {
|
||||
val (zpPtrVar, offset) = pointergen.deref(dereference)
|
||||
asmgen.loadIndirectByte(zpPtrVar, offset)
|
||||
return if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
|
||||
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
||||
}
|
||||
|
||||
@@ -87,6 +98,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bmi", target)
|
||||
}
|
||||
else
|
||||
@@ -94,6 +106,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bpl", target)
|
||||
}
|
||||
else
|
||||
@@ -107,6 +120,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvs", target)
|
||||
}
|
||||
else
|
||||
@@ -114,6 +128,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvc", target)
|
||||
}
|
||||
else
|
||||
@@ -146,6 +161,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", ifElse)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(stmt.hasElse()) {
|
||||
// if and else blocks
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
branchElse(elseLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
branchElse(afterIfLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
}
|
||||
asmgen.out(afterIfLabel)
|
||||
}
|
||||
|
||||
private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
|
||||
fun branchTarget(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
|
||||
var target = asmgen.getJumpTarget(jump, false)
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(target.indirect) {
|
||||
branchElse("+")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
branchTarget(target.asmLabel)
|
||||
}
|
||||
asmgen.translate(elseBlock)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||
// comparison value is already in A
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
@@ -154,7 +256,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -171,10 +273,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" $falseBranch +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||
@@ -210,40 +311,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||
"<=" -> {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
"<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
|
||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||
">=" -> {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
@@ -286,7 +358,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -306,7 +379,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -353,7 +426,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | bne ++")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -375,7 +449,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
bpl $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -402,13 +476,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
@@ -416,25 +490,43 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
if(signed) {
|
||||
// X>Y --> Y<X
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||
asmgen.cmpAwithByteValue(condition.left, true)
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null) {
|
||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bcc + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -453,7 +545,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -466,6 +558,23 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
@@ -535,7 +644,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -562,7 +672,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -590,7 +700,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
cmp $valueLsb
|
||||
bcs +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
_jump jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -615,7 +726,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcs $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -667,7 +778,8 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -694,7 +806,7 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -721,7 +833,8 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -744,7 +857,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -784,7 +897,9 @@ _jump jmp (${target.asmLabel})
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
@@ -800,8 +915,8 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
} else {
|
||||
var varname = asmgen.asmVariableName(value.identifier)
|
||||
if(value.identifier.type.isSplitWordArray) {
|
||||
var varname = asmgen.asmVariableName(value.identifier!!)
|
||||
if(value.identifier!!.type.isSplitWordArray) {
|
||||
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
|
||||
}
|
||||
asmgen.out(" lda #>$varname")
|
||||
@@ -829,7 +944,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -857,7 +973,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -878,8 +994,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -908,7 +1026,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -936,7 +1055,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -973,7 +1092,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1001,7 +1121,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1023,8 +1143,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -1053,7 +1175,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1081,7 +1204,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1136,7 +1259,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
|
||||
}
|
||||
@@ -1157,7 +1282,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if (constIndex != null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
|
||||
}
|
||||
@@ -1196,7 +1323,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1221,7 +1349,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1248,7 +1376,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1273,7 +1402,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1302,7 +1431,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1331,7 +1461,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1361,7 +1491,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1390,7 +1521,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1423,7 +1554,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1452,7 +1584,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1481,7 +1613,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1510,7 +1643,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1532,8 +1665,10 @@ _jump jmp (${target.asmLabel})
|
||||
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
|
||||
val constIndex = left.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if(left.variable==null)
|
||||
TODO("support for ptr indexing ${left.position}")
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varName = asmgen.asmVariableName(left.variable)
|
||||
val varName = asmgen.asmVariableName(left.variable!!)
|
||||
if(left.splitWords) {
|
||||
return if(notEquals)
|
||||
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
|
||||
@@ -1596,13 +1731,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier.name
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
@@ -1648,13 +1783,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier.name
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
|
||||
@@ -9,24 +9,26 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
||||
|
||||
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
||||
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
||||
val endLabel = asmgen.makeLabel("ifexpr_end")
|
||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@@ -42,22 +44,90 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
if (condition is PtBinaryExpression) {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
internal fun assignBranchCondExpression(target: AsmAssignTarget, expr: PtBranchCondExpression) {
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
|
||||
if(target.kind==TargetStorageKind.REGISTER && target.datatype.isUnsignedByte && target.register==RegisterOrPair.A) {
|
||||
if(expr.condition==BranchCondition.CC) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
}
|
||||
else if(expr.condition==BranchCondition.CS) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(condition is PtPrefix && condition.operator=="not") {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
} else {
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
|
||||
val trueLabel = asmgen.makeLabel("branchexpr_true")
|
||||
val endLabel = asmgen.makeLabel("branchexpr_end")
|
||||
val branch = asmgen.branchInstruction(expr.condition, false)
|
||||
|
||||
asmgen.out(" $branch $trueLabel")
|
||||
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
when (condition) {
|
||||
is PtBinaryExpression -> {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
is PtPrefix if condition.operator=="not" -> {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
}
|
||||
|
||||
else -> {
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +152,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
asmgen.out(" beq $falseLabel")
|
||||
} else {
|
||||
@@ -190,7 +260,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne +
|
||||
@@ -214,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp $varRight+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne $falseLabel
|
||||
@@ -237,7 +307,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
@@ -260,7 +330,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp #>$number
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out( """
|
||||
cmp #<$number
|
||||
bne $falseLabel
|
||||
@@ -279,7 +349,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||
}
|
||||
}
|
||||
@@ -294,7 +364,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
|
||||
structInstances2asm()
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ internal class ProgramAndVarsGen(
|
||||
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("P8ZP_SCRATCH_PTR = ${zp.SCRATCH_PTR} ; word")
|
||||
if(compTarget.name=="c64") {
|
||||
if(options.floats)
|
||||
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
|
||||
@@ -208,39 +209,20 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
for (slab in symboltable.allMemorySlabs) {
|
||||
if (slab.align > 1u)
|
||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
||||
}
|
||||
asmgen.out("\t.bend\n .send slabs_BSS")
|
||||
asmgen.out("\t.bend\n .send BSS_SLABS")
|
||||
}
|
||||
}
|
||||
|
||||
private fun tempVars() {
|
||||
asmgen.out("; expression temp vars\n .section BSS")
|
||||
for((dt, count) in asmgen.tempVarsCounters) {
|
||||
if(count>0) {
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.BYTE -> asmgen.out("$name .char ?")
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.WORD -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
asmgen.out(" .dsection STRUCTINSTANCES\n")
|
||||
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
@@ -296,28 +278,34 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
|
||||
}
|
||||
}
|
||||
|
||||
if(relocatedBssEnd >= options.memtopAddress)
|
||||
options.memtopAddress = relocatedBssEnd+1u
|
||||
|
||||
asmgen.out(" ; memtop check")
|
||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||
}
|
||||
@@ -344,9 +332,10 @@ internal class ProgramAndVarsGen(
|
||||
if (initializers.isNotEmpty()) {
|
||||
asmgen.out("prog8_init_vars\t.block")
|
||||
initializers.forEach { assign ->
|
||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
val constvalue = assign.value as? PtNumber
|
||||
if(constvalue==null || constvalue.number!=0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
asmgen.translate(assign)
|
||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
||||
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
@@ -363,7 +352,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -377,11 +366,93 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
private fun asmTypeString(dt: DataType): String {
|
||||
return when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte"
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun structInstances2asm() {
|
||||
|
||||
fun initValues(instance: StStructInstance): List<String> {
|
||||
val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
|
||||
return structtype.fields.zip(instance.initialValues).map { (field, value) ->
|
||||
if(field.first.isFloat) {
|
||||
"["+compTarget.getFloatAsmBytes(value.number!!)+"]"
|
||||
} else {
|
||||
when {
|
||||
value.number!=null -> {
|
||||
if(field.first.isPointer)
|
||||
"$"+value.number!!.toInt().toString(16)
|
||||
else if(field.first.isInteger)
|
||||
value.number!!.toInt().toString()
|
||||
else
|
||||
value.number.toString()
|
||||
}
|
||||
value.addressOfSymbol!=null -> value.addressOfSymbol!!
|
||||
value.boolean!=null -> if(value.boolean==true) "1" else "0"
|
||||
else -> throw AssemblyError("weird struct initial value $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asmgen.out("; struct types")
|
||||
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
|
||||
asmgen.out("${it.structName} .struct $structargs\n")
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
val type = when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
|
||||
}
|
||||
asmgen.out(" .endstruct\n")
|
||||
}
|
||||
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
|
||||
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||
asmgen.out(" .section BSS\n")
|
||||
instancesNoInit.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val zerovalues = structtype.fields.map { field ->
|
||||
if(field.first.isFloat) {
|
||||
val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
|
||||
"[${floatbytes.joinToString(",")}]"
|
||||
}
|
||||
else "?"
|
||||
}
|
||||
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .send BSS\n")
|
||||
|
||||
asmgen.out("; struct instances with initialization values\n")
|
||||
asmgen.out(" .section STRUCTINSTANCES\n")
|
||||
instances.forEach { asmgen.out("${it.name} .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n") }
|
||||
asmgen.out(" .send STRUCTINSTANCES\n")
|
||||
}
|
||||
|
||||
internal fun translateAsmSubroutine(sub: PtAsmSub) {
|
||||
if(sub.inline) {
|
||||
return // subroutine gets inlined at call site.
|
||||
@@ -431,7 +502,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -449,7 +520,7 @@ internal class ProgramAndVarsGen(
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||
|
||||
@@ -485,14 +556,14 @@ internal class ProgramAndVarsGen(
|
||||
sub.children.forEach { asmgen.translate(it) }
|
||||
|
||||
asmgen.out("; variables")
|
||||
asmgen.out(" .section BSS")
|
||||
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||
if(addr!=null)
|
||||
asmgen.out("$name = $addr")
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
@@ -501,11 +572,11 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
asmgen.out(" .send BSS")
|
||||
asmgen.out(" .send BSS_NOCLEAR")
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
@@ -560,12 +631,12 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||
outputStringvar(varname, 0u, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("+")
|
||||
@@ -629,22 +700,37 @@ internal class ProgramAndVarsGen(
|
||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||
if(varsNoInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables")
|
||||
asmgen.out(" .section BSS")
|
||||
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||
|
||||
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||
asmgen.out(" .section $section")
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0u }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
|
||||
.forEach { uninitializedVariable2asm(it) }
|
||||
asmgen.out(" .send $section")
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
|
||||
if(clean.isNotEmpty()) {
|
||||
// clean vars end up in BSS so they're at least cleared to 0 at startup
|
||||
generate("BSS", clean)
|
||||
}
|
||||
if(dirty.isNotEmpty()) {
|
||||
// Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
|
||||
// but NOT at each entry of the subroutine they're declared in.
|
||||
// This saves the STZ's instructions in the subroutine, while still having deterministic start state.
|
||||
// So there is no actual difference here when compared to the way non-dirty variables are allocated.
|
||||
generate("BSS", dirty)
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
if(varsWithInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables with init value")
|
||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
|
||||
notAlignedStrings.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
@@ -652,7 +738,7 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
alignedStrings.sortedBy { it.align }.forEach {
|
||||
outputStringvar(
|
||||
@@ -661,16 +747,22 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
|
||||
notAlignedOther.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,23 +777,27 @@ internal class ProgramAndVarsGen(
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
|
||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||
}
|
||||
dt.isArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun alignVar(align: Int) {
|
||||
if(align > 1)
|
||||
private fun alignVar(align: UInt) {
|
||||
if(align > 1u)
|
||||
asmgen.out(" .align ${align.toHex()}")
|
||||
}
|
||||
|
||||
@@ -738,7 +834,7 @@ internal class ProgramAndVarsGen(
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
dt.isArray -> {
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -746,7 +842,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
|
||||
alignVar(align)
|
||||
when {
|
||||
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||
@@ -848,7 +944,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||
private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
|
||||
alignVar(align)
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
@@ -913,7 +1009,7 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ 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.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
zeropageVars = zeropage.allocatedVariables
|
||||
}
|
||||
|
||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
||||
internal fun isZpVar(scopedName: String): Boolean {
|
||||
if(scopedName in zeropageVars)
|
||||
return true
|
||||
|
||||
val v = symboltable.lookup(scopedName)
|
||||
return if(v is StMemVar) v.address <= 255u else false
|
||||
}
|
||||
|
||||
internal fun getFloatAsmConst(number: Double): String {
|
||||
val asmName = globalFloatConsts[number]
|
||||
@@ -49,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
|
||||
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
@@ -57,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@@ -76,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@@ -89,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
// try to allocate the "don't care" interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
@@ -17,6 +20,8 @@ internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
if(expr.operator==".")
|
||||
throw AssemblyError("pointer deref expression should have been handled elsewhere ${expr.position}")
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
|
||||
@@ -43,6 +48,12 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
expr.type.isPointer -> {
|
||||
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
|
||||
"both operands must be pointers or uwords"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
@@ -50,17 +61,17 @@ internal class AnyExprAsmGen(
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -73,25 +84,25 @@ internal class AnyExprAsmGen(
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -197,7 +208,7 @@ internal class AnyExprAsmGen(
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
VARIABLE, // non-pointer variable
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -32,6 +33,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val pointer: PtPointerDeref? = null,
|
||||
val origAstTarget: PtAssignTarget? = null
|
||||
)
|
||||
{
|
||||
@@ -39,20 +41,31 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if(register!=null && !datatype.isNumericOrBool)
|
||||
throw AssemblyError("must be numeric type")
|
||||
if(kind==TargetStorageKind.REGISTER)
|
||||
require(register!=null)
|
||||
else
|
||||
require(register==null)
|
||||
if(kind==TargetStorageKind.POINTER)
|
||||
require(pointer!=null)
|
||||
if(pointer!=null)
|
||||
require(kind==TargetStorageKind.POINTER)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||
return targets.map {
|
||||
if(it.void)
|
||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.forDt(BaseDataType.UNDEFINED), null, it.position)
|
||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||
else
|
||||
fromAstAssignment(it, definingSub, asmgen)
|
||||
}
|
||||
@@ -77,6 +90,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||
pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@@ -87,18 +101,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
|
||||
val dt = if(signed) DataType.BYTE else DataType.UBYTE
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.FAC1,
|
||||
RegisterOrPair.FAC2 -> {
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
@@ -116,7 +130,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
}
|
||||
@@ -134,11 +148,14 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
left is PtIdentifier && left.name==scopedName
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("is pointer deref target same as expression? ${this.position}")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> false
|
||||
TargetStorageKind.VOID -> false
|
||||
}
|
||||
@@ -160,8 +177,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
@@ -170,7 +190,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
val bv = value as? PtBool
|
||||
if(bv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
@@ -191,7 +211,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||
@@ -203,8 +223,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.returns.size>1)
|
||||
DataType.forDt(BaseDataType.UNDEFINED) // TODO list of types instead?
|
||||
if(sub is PtSub && sub.signature.returns.size>1)
|
||||
DataType.UNDEFINED // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+108
-67
@@ -9,6 +9,7 @@ import prog8.codegen.cpu6502.VariableAllocator
|
||||
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val ptrgen: PointerAssignmentsGen,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
|
||||
@@ -84,22 +85,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isWord -> {
|
||||
target.datatype.isWord || target.datatype.isPointer -> {
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
val targetDt = if(target.datatype.isWord) target.datatype else DataType.UWORD // pointers themselves that get a new value are just treated as UWORD variables
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, target.datatype, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!, block)
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, regName(value), value.datatype, block)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, targetDt, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.array!!, block)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression, block)
|
||||
}
|
||||
else {
|
||||
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block)
|
||||
inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression!!, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,18 +133,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
is PtNumber -> {
|
||||
val addr = (memory.address as PtNumber).number.toInt()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression)
|
||||
} else {
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +172,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||
return
|
||||
// slower method to calculate and use the pointer to access the memory with:
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
@@ -196,21 +198,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
SourceStorageKind.MEMORY -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.memory!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.ARRAY -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx $tempVar")
|
||||
}
|
||||
}
|
||||
@@ -224,7 +226,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
TODO("inplace modification array indexed pointer deref ${target.position}")
|
||||
return
|
||||
}
|
||||
val targetArrayVar = target.array.variable!!
|
||||
val indexNum = target.array.index as? PtNumber
|
||||
if (indexNum!=null) {
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
@@ -301,7 +309,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.datatype.isPointer -> TODO("inplace modification of pointer array ${target.position}")
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
}
|
||||
}
|
||||
@@ -320,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
|
||||
@@ -356,7 +364,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
|
||||
@@ -366,7 +374,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y")
|
||||
}
|
||||
|
||||
target.datatype.isWord -> {
|
||||
@@ -377,11 +385,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
@@ -439,7 +447,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
|
||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||
@@ -450,14 +458,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
|
||||
else
|
||||
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
// copy array value into tempvar
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, target.array)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>${target.asmVarname}
|
||||
@@ -504,18 +512,24 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
pla ; restore array ptr lsb
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
|
||||
target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
|
||||
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
|
||||
val arrayvar = asmgen.asmVariableName(array.variable)
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
TODO("indexed inc/dec on pointer ${array.position}")
|
||||
return false
|
||||
}
|
||||
val arrayvar = asmgen.asmVariableName(arrayVar)
|
||||
when {
|
||||
array.type.isByte -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
|
||||
@@ -571,6 +585,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(if(operator=="+") " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
return true
|
||||
}
|
||||
array.type.isPointer -> TODO("indexed inc/dec on pointer ${array.position}")
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
@@ -583,7 +598,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||
return (address.left as PtIdentifier).name
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
|
||||
return "P8ZP_SCRATCH_W2"
|
||||
}
|
||||
@@ -592,7 +607,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
fun assignValueToA() {
|
||||
val assignValue = AsmAssignment(value,
|
||||
listOf(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.UBYTE),
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
|
||||
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A)
|
||||
),
|
||||
program.memsizer, Position.DUMMY)
|
||||
@@ -635,9 +650,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
if(rightTc!=null)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
|
||||
else
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
|
||||
asmgen.out(" pha") // offset on stack
|
||||
val zpPointerVarName = addrIntoZpPointer()
|
||||
assignValueToA()
|
||||
@@ -884,7 +899,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
|
||||
if (target.datatype == value.type) {
|
||||
if (target.datatype == value.type || (target.datatype.isPointer && value.type.isWord)) {
|
||||
val childDt = value.value.type
|
||||
if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) {
|
||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||
@@ -898,7 +913,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -910,7 +925,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
||||
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
@@ -962,10 +977,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(value==1) {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
asmgen.romableWarning("self-modifying code (access pointer)", pointervar.position) // TODO
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" inc a")
|
||||
else
|
||||
asmgen.out(" clc | adc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tinc $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" clc | adc #$value")
|
||||
@@ -974,10 +997,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"-" -> {
|
||||
if(value==1) {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
asmgen.romableWarning("self-modifying code (access pointer)", pointervar.position) // TODO
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" dec a")
|
||||
else
|
||||
asmgen.out(" sec | sbc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tdec $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" sec | sbc #$value")
|
||||
@@ -1003,7 +1034,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"<<" -> {
|
||||
@@ -1109,11 +1140,18 @@ $shortcutLabel:""")
|
||||
}
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
|
||||
// use the already existing optimized codegen for regular assignments x += array[index]
|
||||
val binexpr = PtBinaryExpression(operator, dt, value.position)
|
||||
binexpr.add(PtIdentifier(name, dt, value.position))
|
||||
val arrayValue = PtArrayIndexer(value.type, value.position)
|
||||
arrayValue.add(value.variable)
|
||||
arrayValue.add(valueVar)
|
||||
arrayValue.add(value.index)
|
||||
binexpr.add(arrayValue)
|
||||
binexpr.parent = value
|
||||
@@ -1174,7 +1212,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@@ -1347,7 +1385,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@@ -1518,7 +1556,7 @@ $shortcutLabel:""")
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
ldy #$value
|
||||
jsr prog8_math.divmod_ub_asm
|
||||
jsr prog8_math.remainder_ub_asm
|
||||
sta $name""")
|
||||
}
|
||||
"<<" -> {
|
||||
@@ -1905,18 +1943,16 @@ $shortcutLabel:""")
|
||||
asmgen.out(" lda #0 | sta $lsb")
|
||||
}
|
||||
value==7 -> {
|
||||
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
|
||||
// optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
|
||||
asmgen.out("""
|
||||
; shift left 7
|
||||
lsr $msb
|
||||
php ; save carry
|
||||
lda $lsb
|
||||
ror a
|
||||
sta $msb
|
||||
lda #0
|
||||
sta $lsb
|
||||
plp ; restore carry
|
||||
ror $msb
|
||||
ror $lsb""")
|
||||
ror a
|
||||
sta $lsb""")
|
||||
}
|
||||
value>3 -> asmgen.out("""
|
||||
ldy #$value
|
||||
@@ -2242,7 +2278,7 @@ $shortcutLabel:""")
|
||||
|
||||
private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
|
||||
require(dt.isWord)
|
||||
require(valueDt.isInteger)
|
||||
require(valueDt.isInteger || valueDt.isPointer)
|
||||
when {
|
||||
valueDt.isByte -> {
|
||||
// the other variable is a BYTE type so optimize for that
|
||||
@@ -2452,7 +2488,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
@@ -2724,7 +2760,7 @@ $shortcutLabel:""")
|
||||
"+" -> {
|
||||
// name += byteexpression
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
@@ -2874,7 +2910,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
@@ -2889,7 +2925,12 @@ $shortcutLabel:""")
|
||||
"-" -> {
|
||||
if(value.index.type.isByte) {
|
||||
// it's an array indexed by a byte so we can use sbc array,y
|
||||
val arrayname = value.variable.name
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
val arrayname = valueVar.name
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords) {
|
||||
asmgen.out("""
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,7 +5,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
@@ -68,6 +70,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -5,12 +5,15 @@ import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.Path
|
||||
|
||||
@@ -52,9 +55,10 @@ class TestCodegen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@@ -64,6 +68,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@@ -73,15 +78,17 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
))
|
||||
sub.add(PtVariable(
|
||||
"xx",
|
||||
DataType.forDt(BaseDataType.WORD),
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@@ -89,13 +96,13 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
}
|
||||
it.add(targetIdx)
|
||||
}
|
||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
assign.add(target)
|
||||
@@ -104,15 +111,15 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||
@@ -120,10 +127,10 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||
sub.add(cxregAssign)
|
||||
|
||||
block.add(sub)
|
||||
@@ -131,7 +138,7 @@ class TestCodegen: FunSpec({
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
val options = getTestOptions()
|
||||
@@ -160,5 +167,15 @@ class TestCodegen: FunSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("memory mapped zp var is correctly considered to be zp var") {
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val st = SymbolTable(program)
|
||||
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
|
||||
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
|
||||
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
|
||||
allocator.isZpVar("zpmemvar") shouldBe true
|
||||
allocator.isZpVar("normalmemvar") shouldBe false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -11,7 +9,7 @@ dependencies {
|
||||
implementation(project(":codeGenIntermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
@@ -26,7 +26,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
if(!options.quiet)
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
return EmptyProgram
|
||||
}
|
||||
}
|
||||
@@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
private object EmptyProgram : IAssemblyProgram {
|
||||
override val name = "<Empty Program>"
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
println("** nothing assembled **")
|
||||
if(!options.quiet)
|
||||
println("** nothing assembled **")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -11,12 +9,10 @@ dependencies {
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,10 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -26,9 +28,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
"peek" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekbool" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebool" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
@@ -45,6 +50,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -78,7 +85,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@@ -91,7 +97,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentA = exprGen.translateExpression(call.args[2])
|
||||
@@ -143,7 +148,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
@@ -240,11 +244,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
@@ -278,7 +282,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
@@ -497,8 +500,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val labelname = SymbolTable.labelnameForStructInstance(call)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = labelname)
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -548,7 +559,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
val variable = arr.variable.name
|
||||
if(arr.variable==null)
|
||||
TODO("support for ptr indexing ${arr.position}")
|
||||
val variable = arr.variable!!.name
|
||||
if(arr.splitWords) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
when(opcodeMemAndReg.first) {
|
||||
@@ -617,7 +630,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(target.variable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -651,6 +666,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
else {
|
||||
val targetVariable = target.variable ?: TODO("support for ptr indexing ${target.position}")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(isConstZeroValue) {
|
||||
@@ -659,17 +676,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -680,17 +697,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StSub
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
@@ -30,7 +27,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
||||
return when (expr) {
|
||||
is PtIrRegister -> {
|
||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||
if(expr.type.isFloat)
|
||||
ExpressionCodeResult(emptyList(), IRDataType.FLOAT, -1, expr.register)
|
||||
else
|
||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||
}
|
||||
is PtBool -> {
|
||||
val code = IRCodeChunk(null, null)
|
||||
@@ -83,15 +83,111 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
is PtArrayIndexer -> translate(expr)
|
||||
is PtBinaryExpression -> translate(expr)
|
||||
is PtIfExpression -> translate(expr)
|
||||
is PtBranchCondExpression -> translate(expr)
|
||||
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
|
||||
is PtFunctionCall -> translate(expr)
|
||||
is PtContainmentCheck -> translate(expr)
|
||||
is PtPointerDeref -> translate(expr)
|
||||
is PtRange,
|
||||
is PtArray,
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
var pointerReg: Int
|
||||
|
||||
if(deref.startpointer.type.isStructInstance) {
|
||||
TODO("translate structinstance deref???")
|
||||
/*
|
||||
val arrayIndexer = deref.startpointer as? PtArrayIndexer
|
||||
if(arrayIndexer==null)
|
||||
throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
|
||||
// first evaluate the adress of POINTER[x].a
|
||||
// which is: value in pointer + x*sizeof(struct) + offsetof(a)
|
||||
// then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
|
||||
val struct = deref.startpointer.type.subType as StStruct
|
||||
val chain = ArrayDeque(deref.chain)
|
||||
val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
|
||||
val pointerTr = translateExpression(arrayIndexer.variable)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
val constIndex = arrayIndexer.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val offset = constIndex * struct.size.toInt()
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
|
||||
} else {
|
||||
val indexTr = translateExpression(arrayIndexer.index)
|
||||
result += indexTr.chunks
|
||||
// multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
|
||||
// and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val indexReg: Int
|
||||
if(arrayIndexer.index.type.isByte) {
|
||||
// extend array index to word
|
||||
indexReg = codeGen.registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
|
||||
} else {
|
||||
indexReg = indexTr.resultReg
|
||||
}
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
|
||||
}
|
||||
}
|
||||
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
|
||||
if (firstField.first.isPointer) {
|
||||
// get the address stored in the pointer and use that for the rest of the chain
|
||||
// LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
} else {
|
||||
require(chain.isEmpty())
|
||||
// it's a pointer to a simple value, so keep the pointer as-is
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field or on b.c.d.field if field isn't a field.
|
||||
val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
|
||||
actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
|
||||
actualDeref.add(arrayIndexer.variable)
|
||||
*/
|
||||
} else {
|
||||
val tr = translateExpression(deref.startpointer)
|
||||
result += tr.chunks
|
||||
pointerReg = tr.resultReg
|
||||
}
|
||||
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
|
||||
result += instructions
|
||||
if(offset<=0u) {
|
||||
val irdt = irType(deref.type)
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
// load field with offset
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val irdt = irType(deref.type)
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
|
||||
|
||||
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
||||
@@ -158,48 +254,142 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(branchExpr: PtBranchCondExpression): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val trueTr = translateExpression(branchExpr.truevalue)
|
||||
val falseTr = translateExpression(branchExpr.falsevalue)
|
||||
val trueLabel = codeGen.createLabelName()
|
||||
val endLabel = codeGen.createLabelName()
|
||||
val irDt = irType(branchExpr.type)
|
||||
|
||||
if(branchExpr.condition==BranchCondition.CC && irDt==IRDataType.BYTE) {
|
||||
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
else if(branchExpr.condition==BranchCondition.CS) {
|
||||
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
|
||||
result.add(IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
|
||||
})
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
val branchInstr = codeGen.IRBranchInstr(branchExpr.condition, trueLabel)
|
||||
addInstr(result, branchInstr, null)
|
||||
|
||||
if (irDt != IRDataType.FLOAT) {
|
||||
addToResult(result, falseTr, trueTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
result += IRCodeChunk(trueLabel, null)
|
||||
addToResult(result, trueTr, trueTr.resultReg, -1)
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||
} else {
|
||||
addToResult(result, falseTr, -1, trueTr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
result += IRCodeChunk(trueLabel, null)
|
||||
addToResult(result, trueTr, -1, trueTr.resultFpReg)
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return ExpressionCodeResult(result, irDt, -1, trueTr.resultFpReg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(expr: PtAddressOf): ExpressionCodeResult {
|
||||
val vmDt = irType(expr.type)
|
||||
val symbol = expr.identifier.name
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
val identifier = expr.identifier
|
||||
|
||||
fun loadAddressOfArrayLabel(reg: Int) {
|
||||
if (expr.isMsbForSplitArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
|
||||
} else if (expr.identifier.type.isSplitWordArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
|
||||
} else if (identifier!!.type.isSplitWordArray) {
|
||||
// the _lsb split array comes first in memory
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
|
||||
} else
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
|
||||
}
|
||||
|
||||
if(expr.isFromArrayElement) {
|
||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
|
||||
if(expr.identifier.type.isUnsignedWord) {
|
||||
val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
|
||||
val ixWord = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
|
||||
ixWord
|
||||
} else indexTr.resultReg
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
if(identifier!!.type.isUnsignedWord) {
|
||||
require(!expr.isMsbForSplitArray)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
val ptr = codeGen.symbolTable.lookup(identifier.name)
|
||||
it += if(ptr is StConstant)
|
||||
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
} else if(identifier.type.isPointer) {
|
||||
// apply pointer arithmetic for the array indexing
|
||||
val eltSize = if(identifier.type.sub!=null)
|
||||
codeGen.program.memsizer.memorySize(identifier.type.sub!!)
|
||||
else
|
||||
identifier.type.subType!!.memsize(codeGen.program.memsizer)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name), null)
|
||||
if (eltSize > 1) {
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
// regular array indexing
|
||||
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else if(expr.identifier!=null ) {
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
val pointerTr = translateExpression(expr.dereference!!.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
|
||||
result += instructions
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
|
||||
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
}
|
||||
|
||||
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
||||
@@ -213,31 +403,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
val ptrWithOffset = mem.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// LOADIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
|
||||
val tr = translateExpression(mem.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -283,7 +474,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val haystackVar = check.haystackHeapVar!!
|
||||
when {
|
||||
haystackVar.type.isString -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -294,7 +484,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isByteArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -308,7 +497,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isWordArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -322,7 +510,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isFloatArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -340,11 +527,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
|
||||
if(arrayIx.type.isStructInstance)
|
||||
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
|
||||
val vmDt = irType(arrayIx.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
val arrayVar = arrayIx.variable
|
||||
if(arrayVar==null) {
|
||||
val pointerTr = translateExpression(arrayIx.pointerderef!!)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
if(arrayVar.type.isPointer) {
|
||||
val pointerTr = translateExpression(arrayVar)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
|
||||
require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
val arrayVarSymbol = arrayVar.name
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
@@ -371,9 +577,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
|
||||
}
|
||||
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||
fun indexByNumber(index: Int) {
|
||||
val memOffset = index * eltSize
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
@@ -382,23 +587,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
}
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
}
|
||||
|
||||
fun indexByExpression() {
|
||||
val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
|
||||
result += code
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber)
|
||||
indexByNumber((arrayIx.index as PtNumber).number.toInt())
|
||||
else
|
||||
indexByExpression()
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translatePointerIndexing(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
pointerReg: Int,
|
||||
index: PtExpression,
|
||||
eltSize: Int,
|
||||
resultDt: IRDataType
|
||||
): ExpressionCodeResult {
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
|
||||
if(index is PtNumber) {
|
||||
val memOffset = eltSize * index.number.toInt()
|
||||
if(memOffset>0)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
|
||||
}
|
||||
else {
|
||||
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
|
||||
}
|
||||
|
||||
if(resultDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(resultDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translate(expr: PtPrefix): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = translateExpression(expr.value)
|
||||
@@ -439,9 +682,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isFloat -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -450,7 +693,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.UBYTE -> {
|
||||
@@ -466,7 +709,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
@@ -482,7 +725,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
@@ -504,7 +747,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
BaseDataType.POINTER -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -526,7 +772,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
@@ -544,49 +790,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
BaseDataType.POINTER -> {
|
||||
require(valueDt.isUnsignedWord || valueDt.isPointer)
|
||||
actualResultReg2 = tr.resultReg
|
||||
// no further conversion required, pointers are all just uwords
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
val vmDt = irType(binExpr.left.type)
|
||||
val signed = binExpr.left.type.isSigned
|
||||
return when(binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, vmDt)
|
||||
"/" -> operatorDivide(binExpr, vmDt, signed)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
"^", "xor" -> operatorXor(binExpr, vmDt)
|
||||
"or" -> operatorOr(binExpr, vmDt, false)
|
||||
"and" -> operatorAnd(binExpr, vmDt, false)
|
||||
"<<" -> operatorShiftLeft(binExpr, vmDt)
|
||||
">>" -> operatorShiftRight(binExpr, vmDt, signed)
|
||||
"==" -> operatorEquals(binExpr, vmDt, false)
|
||||
"!=" -> operatorEquals(binExpr, vmDt, true)
|
||||
"<" -> operatorLessThan(binExpr, vmDt, signed, false)
|
||||
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
|
||||
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
|
||||
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
if(binExpr.operator==".") {
|
||||
return operatorDereference(binExpr) // eww, nasty, would rather not have any such expressions anymore
|
||||
} else {
|
||||
val vmDt = irType(binExpr.left.type)
|
||||
return when (binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
"^", "xor" -> operatorXor(binExpr, vmDt)
|
||||
"or" -> operatorOr(binExpr, vmDt, false)
|
||||
"and" -> operatorAnd(binExpr, vmDt, false)
|
||||
"<<" -> operatorShiftLeft(binExpr, vmDt)
|
||||
">>" -> operatorShiftRight(binExpr, vmDt, signed)
|
||||
"==" -> operatorEquals(binExpr, vmDt, false)
|
||||
"!=" -> operatorEquals(binExpr, vmDt, true)
|
||||
"<" -> operatorLessThan(binExpr, vmDt, signed, false)
|
||||
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
|
||||
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
|
||||
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
|
||||
|
||||
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
|
||||
return translateStackFunctions(fcall, callTarget)
|
||||
}
|
||||
when(callTarget.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.clear_carry" -> {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(chunk, IRInstruction(Opcode.CLC), null)
|
||||
@@ -612,7 +870,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (callTarget) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -636,7 +893,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// return value(s)
|
||||
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||
// TODO: for current implementation of the call convention in case of multiple return values,
|
||||
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
|
||||
// So we use an empty list to avoid confusion here. This may change in a future version.
|
||||
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
|
||||
@@ -664,7 +921,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
is StExtSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -703,9 +959,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
Opcode.CALL,
|
||||
address = address.address.toInt(),
|
||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||
}
|
||||
else {
|
||||
TODO("callfar into another bank is not implemented for the selected compilation target")
|
||||
} else if(address.constbank!=null) {
|
||||
IRInstruction(
|
||||
Opcode.CALLFAR,
|
||||
address = address.address.toInt(),
|
||||
immediate = address.constbank!!.toInt()
|
||||
)
|
||||
} else {
|
||||
val tr = translateExpression(address.varbank!!)
|
||||
require(tr.dt==IRDataType.BYTE)
|
||||
result += tr.chunks
|
||||
IRInstruction(
|
||||
Opcode.CALLFARVB,
|
||||
address = address.address.toInt(),
|
||||
reg1 = tr.resultReg
|
||||
)
|
||||
}
|
||||
}
|
||||
addInstr(result, call, null)
|
||||
@@ -756,6 +1024,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
||||
}
|
||||
is StStruct -> {
|
||||
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
|
||||
}
|
||||
else -> {
|
||||
if(callTarget.type == StNodeType.LABEL) {
|
||||
require(fcall.void)
|
||||
@@ -765,7 +1036,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("invalid node type")
|
||||
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -798,7 +1069,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
when(callTarget.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.push" -> {
|
||||
// push byte
|
||||
val tr = translateExpression(fcall.args.single())
|
||||
@@ -1033,7 +1304,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -1048,7 +1319,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -1208,7 +1479,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
@@ -1223,11 +1495,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, if(signed)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
addInstr(result, if(dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@@ -1235,35 +1507,36 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, signed)
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, dt.isSigned)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorLeft = binExpr.left as? PtNumber
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
@@ -1285,7 +1558,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.MULSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@@ -1293,20 +1566,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorLeft.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
val opcode = if(dt.isSigned) Opcode.MULSR else Opcode.MULR
|
||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
@@ -1422,6 +1696,136 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDereference(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
val left = binExpr.left as? PtArrayIndexer
|
||||
val right = binExpr.right as? PtIdentifier
|
||||
require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val field: Pair<DataType, UByte>
|
||||
val pointerReg: Int
|
||||
var extraFieldOffset = 0
|
||||
|
||||
if(left.type.isStructInstance) {
|
||||
|
||||
// indexing on a pointer directly
|
||||
// fetch pointer address, determine struct and field, add index * structsize
|
||||
if(left.variable!=null) {
|
||||
val pointerTr = translateExpression(left.variable!!)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
} else if(left.pointerderef!=null) {
|
||||
TODO("get pointer from deref $left")
|
||||
} else {
|
||||
throw AssemblyError("weird arrayindexer $left")
|
||||
}
|
||||
val struct = left.type.subType!! as StStruct
|
||||
val constindex = left.index as? PtNumber
|
||||
if(constindex!=null) {
|
||||
extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
|
||||
} else {
|
||||
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
|
||||
result += chunks
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
|
||||
}
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
|
||||
} else {
|
||||
|
||||
// indexing on an array with pointers
|
||||
// fetch the pointer from the array, determine the struct & field
|
||||
val indexedTr = translateExpression(left)
|
||||
result += indexedTr.chunks
|
||||
pointerReg = indexedTr.resultReg
|
||||
val struct = left.type.dereference().subType as? StStruct
|
||||
require(indexedTr.dt == IRDataType.WORD && struct != null)
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
}
|
||||
|
||||
// add field offset to pointer and load the value into the result register
|
||||
val fieldVmDt = irType(field.first)
|
||||
val fieldOffset = field.second.toInt() + extraFieldOffset
|
||||
var resultFpReg = -1
|
||||
var resultReg = -1
|
||||
if (fieldVmDt == IRDataType.FLOAT)
|
||||
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
else
|
||||
resultReg = codeGen.registers.next(fieldVmDt)
|
||||
if(fieldOffset==0) {
|
||||
// no offset, can use current pointer directly
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += if (fieldVmDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
|
||||
}
|
||||
} else {
|
||||
// actually have to add an offset
|
||||
if(fieldOffset<=255) {
|
||||
if(fieldVmDt==IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, fpReg1 = resultFpReg, reg1 = pointerReg, immediate = fieldOffset), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, reg1 = resultReg, reg2 = pointerReg, immediate = fieldOffset), null)
|
||||
} else {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldOffset)
|
||||
it += if (fieldVmDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
|
||||
else
|
||||
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, fieldVmDt, resultReg, resultFpReg)
|
||||
}
|
||||
|
||||
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UByte> {
|
||||
// returns instructions to calculate the pointer address, and the offset into the struct it points to
|
||||
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(targetPointerDeref.chain.isEmpty())
|
||||
return result to 0u // nothing to do; there's no deref chain
|
||||
|
||||
var struct: StStruct? = null
|
||||
if(targetPointerDeref.startpointer.type.subType!=null)
|
||||
struct = targetPointerDeref.startpointer.type.subType as StStruct
|
||||
if(targetPointerDeref.chain.isNotEmpty()) {
|
||||
// traverse deref chain
|
||||
for(deref in targetPointerDeref.chain.dropLast(1)) {
|
||||
val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
|
||||
struct = fieldinfo.first.subType as StStruct
|
||||
// get new pointer from field
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val field = targetPointerDeref.chain.last()
|
||||
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
|
||||
if(fieldinfo.second>0u) {
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// add the field offset
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
return result to 0u
|
||||
} else {
|
||||
return result to fieldinfo.second
|
||||
}
|
||||
}
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
}
|
||||
return result to 0u
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class IRCodeGen(
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
@@ -55,6 +56,8 @@ class IRCodeGen(
|
||||
return irProg
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
|
||||
// Normally, block level (global) variables that have a numeric initialization value
|
||||
// are initialized via an assignment statement.
|
||||
@@ -64,15 +67,16 @@ class IRCodeGen(
|
||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||
val block = variable.parent.astNode as PtBlock
|
||||
val initialization = (block.children.firstOrNull {
|
||||
it is PtAssignment && it.target.identifier?.name==variable.scopedName
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
|
||||
} as PtAssignment?)
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
when(val initValue = initialization?.value){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
is PtNumber -> {
|
||||
require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.number)
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
@@ -101,6 +105,15 @@ class IRCodeGen(
|
||||
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
|
||||
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
|
||||
is PtIdentifier -> {
|
||||
if('.' !in node.name) {
|
||||
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
|
||||
val expr = node.parent as? PtBinaryExpression
|
||||
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || (!expr.left.type.isPointer && !expr.left.type.isStructInstance))
|
||||
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
}
|
||||
}
|
||||
else -> { /* node has no name or is ok to have no dots in the name */ }
|
||||
}
|
||||
node.children.forEach { verifyPtNode(it) }
|
||||
@@ -194,10 +207,7 @@ class IRCodeGen(
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
immediate = immediateValue,
|
||||
null,
|
||||
address = addressValue,
|
||||
null,
|
||||
null
|
||||
address = addressValue
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -256,9 +266,11 @@ class IRCodeGen(
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node $node")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
|
||||
is PtStructDecl -> emptyList()
|
||||
is PtSubSignature -> emptyList()
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@@ -324,7 +336,7 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
internal fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
if(label!=null)
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
@@ -420,35 +432,44 @@ class IRCodeGen(
|
||||
whenStmt.choices.children.forEach {
|
||||
val choice = it as PtWhenChoice
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
result += translateNode(choice.statements)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
val branchLabel: String
|
||||
if(onlyJumpLabel==null) {
|
||||
choices.add(choiceLabel to choice)
|
||||
branchLabel = choiceLabel
|
||||
} else {
|
||||
branchLabel = onlyJumpLabel
|
||||
}
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
if(choices.isNotEmpty())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
choices.forEach { (label, choice) ->
|
||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||
val lastStatement = choice.statements.children.last()
|
||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
||||
if(!choice.isOnlyGotoOrReturn())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
}
|
||||
|
||||
@@ -468,11 +489,12 @@ class IRCodeGen(
|
||||
translateForInNonConstantRange(forLoop, loopvar)
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val indexReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(elementDt)
|
||||
val loopLabel = createLabelName()
|
||||
val endLabel = createLabelName()
|
||||
when {
|
||||
@@ -480,9 +502,9 @@ class IRCodeGen(
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
val jumpChunk = IRCodeChunk(null, null)
|
||||
@@ -491,10 +513,9 @@ class IRCodeGen(
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
val elementDt = iterable.type.elementType()
|
||||
if(!elementDt.isWord)
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
@@ -504,7 +525,7 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -548,7 +569,7 @@ class IRCodeGen(
|
||||
val step = iterable.step.number.toInt()
|
||||
if (step==0)
|
||||
throw AssemblyError("step 0")
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -634,7 +655,7 @@ class IRCodeGen(
|
||||
|
||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
|
||||
val loopLabel = createLabelName()
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -771,7 +792,7 @@ class IRCodeGen(
|
||||
code += if(factor==0.0) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -789,38 +810,40 @@ class IRCodeGen(
|
||||
val factorReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
val irdt = irType(dt)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
||||
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
|
||||
val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
|
||||
IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@@ -850,10 +873,11 @@ class IRCodeGen(
|
||||
else {
|
||||
val factorReg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -1070,7 +1094,7 @@ class IRCodeGen(
|
||||
}
|
||||
// evaluate jump address expression into a register and jump indirectly to it
|
||||
val tr = expressionEval.translateExpression(goto.target)
|
||||
for(i in tr.chunks.flatMap { it.instructions }) {
|
||||
for(i in tr.chunks.flatMap { c -> c.instructions }) {
|
||||
it += i
|
||||
}
|
||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||
@@ -1653,7 +1677,7 @@ class IRCodeGen(
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtTypeCast -> {
|
||||
require(cond.type.isBool && cond.value.type.isNumeric)
|
||||
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
|
||||
@@ -1669,6 +1693,9 @@ class IRCodeGen(
|
||||
is PtBinaryExpression -> {
|
||||
translateBinExpr(cond)
|
||||
}
|
||||
is PtPointerDeref -> {
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
@@ -1743,9 +1770,7 @@ class IRCodeGen(
|
||||
private fun isIndirectJump(jump: PtJump): Boolean {
|
||||
if(jump.target.asConstInteger()!=null)
|
||||
return false
|
||||
val identifier = jump.target as? PtIdentifier
|
||||
if(identifier==null)
|
||||
return true
|
||||
val identifier = jump.target as? PtIdentifier ?: return true
|
||||
val symbol = symbolTable.lookup(identifier.name)
|
||||
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
|
||||
}
|
||||
@@ -1761,18 +1786,33 @@ class IRCodeGen(
|
||||
if(ret.children.size>1) {
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
// make sure to assign the first value as the last in the sequence, to avoid clobbering the AY registers afterwards
|
||||
// a floating point value is passed via FAC (just one fp value is possible)
|
||||
|
||||
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
|
||||
val values = ret.children.zip(returnRegs)
|
||||
// first all but the first return values
|
||||
for ((value, register) in values.drop(1)) {
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
// finally do the first of the return values (this avoids clobbering of its value in AY)
|
||||
values.first().also { (value, register) ->
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
return result
|
||||
@@ -1821,7 +1861,7 @@ class IRCodeGen(
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
|
||||
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
@@ -1870,21 +1910,22 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
|
||||
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
|
||||
val result = mutableListOf<IRSubroutine.IRParam>()
|
||||
parameters.forEach {
|
||||
it as PtSubroutineParameter
|
||||
if(it.register==null) {
|
||||
val flattenedName = it.definingISub()!!.name + "." + it.name
|
||||
if (symbolTable.lookup(flattenedName) == null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
result += IRSubroutine.IRParam(flattenedName, orig.dt)
|
||||
require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
|
||||
val orig = symbolTable.lookup(it.name) as? StStaticVariable
|
||||
?: TODO("fix missing lookup for: ${it.name} parameter")
|
||||
result += IRSubroutine.IRParam(it.name, orig.dt)
|
||||
} else {
|
||||
val reg = it.register
|
||||
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||
@@ -1909,7 +1950,7 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
val args = params.map { (dt, reg)->
|
||||
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
|
||||
@@ -1920,9 +1961,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
when(registerOrFlag.registerOrPair) {
|
||||
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
|
||||
@@ -1939,10 +1978,83 @@ class IRCodeGen(
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("weird statusflag as param")
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UByte> {
|
||||
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
|
||||
val pointerTr = expressionEval.translateExpression(deref.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
result += instructions
|
||||
return pointerTr.resultReg to offset
|
||||
}
|
||||
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UByte, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
|
||||
if(offset<=0u) {
|
||||
val irdt = irType(type)
|
||||
val instr = if(type.isFloat) {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
|
||||
} else {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
|
||||
}
|
||||
addInstr(result, instr, null)
|
||||
return
|
||||
}
|
||||
|
||||
// store with field offset
|
||||
var valueRegister = existingValueRegister
|
||||
val irdt = irType(type)
|
||||
if(valueIsZero && valueRegister<0) {
|
||||
if(type.isFloat) {
|
||||
valueRegister = registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
|
||||
} else {
|
||||
valueRegister = registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
|
||||
}
|
||||
}
|
||||
val instr = if (type.isFloat)
|
||||
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
|
||||
addInstr(result, instr, null)
|
||||
}
|
||||
|
||||
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(wordIndex) {
|
||||
val tr = expressionEval.translateExpression(index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
var indexReg = tr.resultReg
|
||||
if(tr.dt==IRDataType.BYTE) {
|
||||
indexReg = registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
|
||||
}
|
||||
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
|
||||
return Pair(result, indexReg)
|
||||
}
|
||||
|
||||
// regular byte size index value.
|
||||
val byteIndexTr = expressionEval.translateExpression(index)
|
||||
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
|
||||
|
||||
if(itemsize==1 || arrayIsSplitWords)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
|
||||
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
@@ -52,6 +53,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
|| simplifyConstantReturns(chunk1, indexedInstructions)
|
||||
|| removeNeedlessLoads(chunk1, indexedInstructions)
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
@@ -84,7 +87,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
|
||||
If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
@@ -111,14 +114,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(index>0) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
removeChunks += index
|
||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
||||
replaceLabels.entries.forEach { (key, value) ->
|
||||
if (value == chunk.label)
|
||||
replaceLabels[key] = nextchunk.label!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,12 +354,35 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeNeedlessLoads(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
/*
|
||||
load.b r2,#2
|
||||
loadr.b r1,r2
|
||||
jump p8_label_gen_2
|
||||
*/
|
||||
var changed=false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>=2 && ins.opcode in OpcodesThatJump) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
val previous2 = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
|
||||
if(previous.reg2==previous2.reg1) {
|
||||
chunk.instructions[idx-2] = previous2.copy(reg1=previous.reg1)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
when (ins.opcode) {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
@@ -384,15 +402,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
Opcode.AND -> {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
when (ins.immediate) {
|
||||
0 -> {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
}
|
||||
255 if ins.type == IRDataType.BYTE -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
65535 if ins.type == IRDataType.WORD -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode.OR -> {
|
||||
@@ -458,4 +480,23 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun simplifyConstantReturns(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// use a RETURNI when a RETURNR is just returning a constant that was loaded into a register just before
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(ins.opcode==Opcode.RETURNR) {
|
||||
if(idx>0) {
|
||||
val insBefore = chunk.instructions[idx-1]
|
||||
if(insBefore.opcode == Opcode.LOAD && insBefore.immediate!=null) {
|
||||
val constvalue = insBefore.immediate!!
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.RETURNI, ins.type, immediate = constvalue)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
|
||||
val prefix = "$blockLabel."
|
||||
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||
|
||||
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
|
||||
|
||||
blockVars.forEach { stVar ->
|
||||
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.forEach { ins ->
|
||||
@@ -47,7 +50,50 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
}
|
||||
|
||||
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
|
||||
blockStructs.forEach { struct ->
|
||||
irprog.st.allStructInstances().forEach { instance ->
|
||||
if(instance.structName == struct.name)
|
||||
return // a struct instance is declared using this struct type
|
||||
}
|
||||
irprog.st.allVariables().forEach { variable ->
|
||||
if(variable.dt.isPointer || variable.dt.isStructInstance)
|
||||
if(struct.name == variable.dt.subType!!.scopedNameString)
|
||||
return // a variable exists with the struct as (pointer) type
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
removeBlockInits(irprog, blockLabel)
|
||||
}
|
||||
|
||||
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
|
||||
val instructions = code.globalInits.instructions
|
||||
instructions.toTypedArray().forEach {ins ->
|
||||
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
|
||||
// remove stray loads
|
||||
instructions.toTypedArray().forEach { ins ->
|
||||
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
|
||||
if(ins.reg1!=0) {
|
||||
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
else if(ins.fpReg1!=0) {
|
||||
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeUnusedSubroutines(): Int {
|
||||
@@ -173,7 +219,7 @@ class IRUnusedCodeRemover(
|
||||
if(chunk!=null)
|
||||
new+=chunk
|
||||
else
|
||||
allLabeledChunks[label]?.let { new += it }
|
||||
allLabeledChunks[label]?.let { c -> new += c }
|
||||
}
|
||||
else
|
||||
new += instr.branchTarget!!
|
||||
@@ -226,7 +272,7 @@ class IRUnusedCodeRemover(
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null) {
|
||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||
allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StArrayElement
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StMemorySlab
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.IRStArrayElement
|
||||
import prog8.intermediate.IRStConstant
|
||||
import prog8.intermediate.IRStMemVar
|
||||
import prog8.intermediate.IRStMemorySlab
|
||||
import prog8.intermediate.IRStStaticVariable
|
||||
import prog8.intermediate.IRSymbolTable
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
@@ -25,6 +14,12 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
StNodeType.STRUCTINSTANCE -> {
|
||||
val instance = it.value as StStructInstance
|
||||
val struct = sourceSt.lookup(instance.structName) as StStruct
|
||||
st.add(convert(instance, struct.fields))
|
||||
}
|
||||
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
@@ -37,7 +32,7 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
val addrOfSymbol = arrayElt.addressOfSymbol
|
||||
if (addrOfSymbol != null) {
|
||||
require(addrOfSymbol.contains('.')) {
|
||||
"pointer var in array should be properly scoped: ${addrOfSymbol} in ${variable.name}"
|
||||
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,16 +43,19 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
val scopedName: String
|
||||
if('.' in variable.name) {
|
||||
scopedName = variable.name
|
||||
return IRStStaticVariable(variable.name,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
@@ -65,7 +63,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align)
|
||||
variable.align,
|
||||
variable.dirty)
|
||||
} else {
|
||||
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||
if(array==null)
|
||||
@@ -74,14 +73,14 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedName))
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
scopedName = variable.scopedName
|
||||
val scopedName = variable.scopedNameString
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
@@ -89,16 +88,15 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align
|
||||
variable.align,
|
||||
variable.dirty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemVar): IRStMemVar {
|
||||
val scopedName: String
|
||||
if('.' in variable.name) {
|
||||
scopedName = variable.name
|
||||
return IRStMemVar(
|
||||
variable.name,
|
||||
variable.dt,
|
||||
@@ -106,8 +104,8 @@ private fun convert(variable: StMemVar): IRStMemVar {
|
||||
variable.length
|
||||
)
|
||||
} else {
|
||||
scopedName = try {
|
||||
variable.scopedName
|
||||
val scopedName = try {
|
||||
variable.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
@@ -122,7 +120,7 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedName
|
||||
constant.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
@@ -135,12 +133,17 @@ private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
else
|
||||
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
|
||||
val values = fields.zip(instance.initialValues).map { (field, value) ->
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?
|
||||
@@ -3,7 +3,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
@@ -66,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -53,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
test("remove jmp to label below") {
|
||||
test("remove jmp to label below but keep labels") {
|
||||
val c1 = IRCodeChunk("main.start", null)
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||
val c2 = IRCodeChunk("label", null)
|
||||
@@ -69,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 3
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label2"
|
||||
irProg.chunks()[2].label shouldBe "label3"
|
||||
irProg.chunks()[0].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe false
|
||||
irProg.chunks()[2].isEmpty() shouldBe true
|
||||
val chunks = irProg.chunks()
|
||||
chunks.size shouldBe 4
|
||||
chunks[0].label shouldBe "main.start"
|
||||
chunks[1].label shouldBe "label"
|
||||
chunks[2].label shouldBe "label2"
|
||||
chunks[3].label shouldBe "label3"
|
||||
chunks[0].isEmpty() shouldBe true
|
||||
chunks[1].isEmpty() shouldBe true
|
||||
chunks[2].isEmpty() shouldBe false
|
||||
chunks[3].isEmpty() shouldBe true
|
||||
val instr = irProg.chunks().flatMap { it.instructions }
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.JUMP
|
||||
|
||||
@@ -49,9 +49,10 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@@ -61,6 +62,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@@ -70,15 +72,17 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
))
|
||||
sub.add(PtVariable(
|
||||
"xx",
|
||||
DataType.forDt(BaseDataType.WORD),
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@@ -86,7 +90,7 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX",
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
Position.DUMMY))
|
||||
@@ -94,7 +98,7 @@ class TestVmCodeGen: FunSpec({
|
||||
}
|
||||
it.add(targetIdx)
|
||||
}
|
||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||
value.add(PtIdentifier("main.start.particleDX",
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
Position.DUMMY))
|
||||
@@ -105,15 +109,15 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||
@@ -121,10 +125,10 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||
sub.add(cxregAssign)
|
||||
|
||||
block.add(sub)
|
||||
@@ -132,7 +136,7 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
val options = getTestOptions()
|
||||
@@ -164,40 +168,41 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -235,40 +240,41 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -302,24 +308,25 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
@@ -357,40 +364,41 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -428,40 +436,41 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -495,24 +504,25 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"ub1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
@@ -543,7 +553,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(extsub)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
sub.add(call)
|
||||
block.add(sub)
|
||||
program.add(block)
|
||||
@@ -554,11 +564,8 @@ class TestVmCodeGen: FunSpec({
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks[0].instructions.size shouldBe 2
|
||||
val preparecallInstr = irChunks[0].instructions[0]
|
||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
||||
preparecallInstr.immediate shouldBe 0
|
||||
val callInstr = irChunks[0].instructions[1]
|
||||
irChunks[0].instructions.size shouldBe 1
|
||||
val callInstr = irChunks[0].instructions[0]
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -8,7 +6,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":compilerAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.isInteger
|
||||
import prog8.code.core.isIntegerOrBool
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
@@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
||||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
|
||||
@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null) {
|
||||
if(addrOf.identifier.inferType(program).isBytes)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
if(addrOf.identifier?.inferType(program)?.isBytes==true)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||
}
|
||||
else {
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
|
||||
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
||||
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
||||
}
|
||||
@@ -332,15 +332,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("constant fold pointer[i]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,12 +391,11 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val rangeTo = iterableRange.to as? NumericLiteral
|
||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
|
||||
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||
require(loopvar.datatype.sub == null)
|
||||
val loopvarSimpleDt = loopvar.datatype.base
|
||||
when(loopvarSimpleDt) {
|
||||
require(loopvar.datatype.isBasic)
|
||||
when(val loopvarSimpleDt = loopvar.datatype.base) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(rangeFrom.type != BaseDataType.UBYTE) {
|
||||
// attempt to translate the iterable into ubyte values
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.UndefinedSymbolError
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
@@ -328,7 +323,7 @@ internal class ConstantIdentifierReplacer(
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
@@ -457,7 +452,7 @@ internal class ConstantIdentifierReplacer(
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
if(constRange!=null) {
|
||||
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
|
||||
val rangeType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
return if(rangeType.isByte) {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
|
||||
@@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (literal != null && typecast.type.isBasic) {
|
||||
val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program) issimpletype typecast.type) {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(truepart.statements.singleOrNull() is Jump) {
|
||||
return listOf(
|
||||
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse)
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
if(elsepart.statements.singleOrNull() is Jump) {
|
||||
@@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
@@ -242,14 +242,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -262,14 +264,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
@@ -423,6 +427,31 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
if((arrayIndexedExpression.parent as? BinaryExpression)?.operator !=".") {
|
||||
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
val ptrDeref = arrayIndexedExpression.pointerderef
|
||||
if(ptrDeref!=null) {
|
||||
val dt = ptrDeref.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// ptr1.ptr2[0] --> ptr1.ptr2^^
|
||||
val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
// NOTE: only when the terms are not function calls!!!
|
||||
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
|
||||
@@ -499,7 +528,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (valueDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
@@ -516,7 +545,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (argDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -531,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCallExpr,
|
||||
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position),
|
||||
NumericLiteral(valueDt.getOr(DataType.UBYTE).base, 0.0, arg.expression.position),
|
||||
parent))
|
||||
}
|
||||
} else {
|
||||
@@ -546,7 +575,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// useless msb() of byte value, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCallExpr,
|
||||
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
|
||||
NumericLiteral(argDt.getOr(DataType.UBYTE).base, 0.0, arg.position),
|
||||
parent))
|
||||
}
|
||||
}
|
||||
@@ -555,12 +584,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
|
||||
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
|
||||
// just cast the lsb to uword
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl()
|
||||
if(target?.value is StringLiteral) {
|
||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
||||
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||
@@ -711,9 +740,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// just use: msb(value) as type
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt.isSignedWord)
|
||||
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
@@ -839,14 +868,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -887,12 +916,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -903,12 +932,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount == 8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
}
|
||||
else if(amount > 8) {
|
||||
// same as above but with residual shifts. Take care to do signed shift.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.size==0
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
// inliner potentially enables *ONE LINED* subroutines, without to be inlined.
|
||||
|
||||
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||
|
||||
@@ -45,7 +45,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
else if (stmt.values[0] is IdentifierReference) {
|
||||
makeFullyScoped(stmt.values[0] as IdentifierReference)
|
||||
true
|
||||
} else if (stmt.values[0]!! is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
} else if (stmt.values[0] is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
if (stmt.values[0] is FunctionCallExpression) {
|
||||
makeFullyScoped(stmt.values[0] as FunctionCallExpression)
|
||||
true
|
||||
@@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
if (subroutine.inline && subroutine.statements.size > 1) {
|
||||
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
|
||||
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
subroutine.statements.removeLastOrNull() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||
identifier.targetStatement(program)?.let { target ->
|
||||
identifier.targetStatement()?.let { target ->
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||
@@ -122,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
@@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
when (it) {
|
||||
is NumericLiteral -> it.copy()
|
||||
is IdentifierReference -> {
|
||||
val target = it.targetStatement(program) ?: return emptyList()
|
||||
val target = it.targetStatement() ?: return emptyList()
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
IdentifierReference(scoped, it.position)
|
||||
}
|
||||
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
return if(sub==null || !canInline(sub, functionCallStatement))
|
||||
noModifications
|
||||
else
|
||||
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
|
||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||
"invalid inline sub at ${sub.position}"
|
||||
@@ -243,13 +243,13 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
|
||||
if(!sub.inline)
|
||||
if (!sub.inline)
|
||||
return false
|
||||
if(options.compTarget.name!=VMTarget.NAME) {
|
||||
if (options.compTarget.name != VMTarget.NAME) {
|
||||
val stmt = sub.statements.single()
|
||||
if (stmt is IFunctionCall) {
|
||||
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
|
||||
return existing !is VarDecl
|
||||
return existing !is VarDecl && existing !is StructFieldRef
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -5,11 +5,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
class StatementOptimizer(private val program: Program,
|
||||
@@ -23,7 +19,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in functions.purefunctionNames) {
|
||||
if("ignore_unused" !in parent.definingBlock.options())
|
||||
errors.info("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
@@ -33,12 +29,19 @@ class StatementOptimizer(private val program: Program,
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
if(arg.arrayIndex==null) arg.identifier else null
|
||||
if(arg.arrayIndex==null) {
|
||||
if(arg.identifier!=null)
|
||||
arg.identifier
|
||||
else
|
||||
null // struct can't have string fields so nothing to look at here
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
|
||||
if(stringVar!=null && stringVar.wasStringLiteral()) {
|
||||
val string = stringVar.targetVarDecl()?.value as? StringLiteral
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
@@ -86,12 +89,11 @@ class StatementOptimizer(private val program: Program,
|
||||
// empty true part? switch with the else part
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse)
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,7 +112,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
@@ -150,13 +152,13 @@ class StatementOptimizer(private val program: Program,
|
||||
if (range.size() == 1) {
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl()
|
||||
if(iterable!=null) {
|
||||
if(iterable.datatype.isString) {
|
||||
val sv = iterable.value as StringLiteral
|
||||
@@ -165,8 +167,8 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@@ -177,9 +179,9 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@@ -315,17 +317,6 @@ class StatementOptimizer(private val program: Program,
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
if(bexpr!=null) {
|
||||
val rightCv = bexpr.right.constValue(program)?.number
|
||||
if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
|
||||
if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
|
||||
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
|
||||
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
|
||||
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bexpr, negation, assignment),
|
||||
IAstModification.InsertAfter(assignment, addValue, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
||||
// assignments of the form: X = X <operator> <expr>
|
||||
@@ -357,6 +348,39 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// pointer arithmetic for 6502 target
|
||||
if (options.compTarget.cpu != CpuType.VIRTUAL) {
|
||||
if(bexpr.operator=="+" && !bexpr.right.isSimple && !assignment.isAugmentable) {
|
||||
if(targetIDt.isUnsignedWord || targetIDt.getOrUndef().isPointerToByte) {
|
||||
val leftDt = bexpr.left.inferType(program).getOrUndef()
|
||||
val rightDt = bexpr.right.inferType(program).getOrUndef()
|
||||
|
||||
fun setSizedValue(a: Assignment, value: Expression, size: Int) {
|
||||
val sized = BinaryExpression(value, "*", NumericLiteral.optimalInteger(size, value.position), value.position)
|
||||
a.value = sized
|
||||
sized.linkParents(a)
|
||||
}
|
||||
|
||||
if (leftDt.isPointer && !leftDt.isPointerToByte) {
|
||||
// uword x = pointer + value --> x=value * sizeof , x += pointer
|
||||
val size = leftDt.size(options.compTarget)
|
||||
setSizedValue(assignment, bexpr.right, size)
|
||||
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position)
|
||||
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
|
||||
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
|
||||
} else if (rightDt.isPointer && !rightDt.isPointerToByte) {
|
||||
// uword x = value + pointer --> x=value * sizeof, x += pointer
|
||||
val size = rightDt.size(options.compTarget)
|
||||
setSizedValue(assignment, bexpr.left, size)
|
||||
assignment.linkParents(parent)
|
||||
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position)
|
||||
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
|
||||
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// word = lsb(word)
|
||||
@@ -481,7 +505,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return IfElse(
|
||||
compare,
|
||||
AnonymousScope(mutableListOf(assign), position),
|
||||
AnonymousScope(mutableListOf(), position),
|
||||
AnonymousScope.empty(),
|
||||
position
|
||||
)
|
||||
}
|
||||
@@ -516,6 +540,27 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(whenStmt.betterAsOnGoto(program, options)) {
|
||||
// rewrite when into a on..goto , which is faster and also smaller for ~5+ cases
|
||||
var elseJump: Jump? = null
|
||||
val jumps = mutableListOf<Pair<Int, Jump>>()
|
||||
whenStmt.choices.forEach { choice ->
|
||||
if(choice.values==null) {
|
||||
elseJump = choice.statements.statements.single() as Jump
|
||||
} else {
|
||||
choice.values!!.forEach { value ->
|
||||
jumps.add(value.constValue(program)!!.number.toInt() to choice.statements.statements.single() as Jump)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jumpLabels = jumps.sortedBy { it.first }.map { it.second.target as IdentifierReference }
|
||||
|
||||
val elsePart = if(elseJump==null) null else AnonymousScope(mutableListOf(elseJump), elseJump.position)
|
||||
val onGoto = OnGoto(false, whenStmt.condition, jumpLabels, elsePart, whenStmt.position)
|
||||
return listOf(IAstModification.ReplaceNode(whenStmt, onGoto, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pushw = subroutines.single { it.name == "pushw" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val popw = subroutines.single { it.name == "popw" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pushw = subroutines.single { s -> s.name == "pushw" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
val popw = subroutines.single { s -> s.name == "popw" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pushw)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
@@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
}
|
||||
@@ -181,14 +181,20 @@ class UnusedCodeRemover(private val program: Program,
|
||||
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
|
||||
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
|
||||
if(declIndex==singleUseIndex-1) {
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
|
||||
if(callStruct!=null) {
|
||||
// don't turn a struct instance allocation call to a void call, instead, remove everything
|
||||
return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
|
||||
} else {
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
@@ -18,18 +16,15 @@ dependencies {
|
||||
implementation(project(":codeGenExperimental"))
|
||||
implementation(project(":virtualmachine"))
|
||||
// implementation(project(":beanshell"))
|
||||
implementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
testImplementation(project(":codeCore"))
|
||||
testImplementation(project(":intermediate"))
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
@@ -83,6 +78,8 @@ gversion {
|
||||
classPackage = "prog8.buildversion"
|
||||
className = "Version"
|
||||
language = "kotlin"
|
||||
debug = false
|
||||
annotate = true
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
@@ -25,6 +23,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,4 +1,7 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
diskio {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
}
|
||||
@@ -317,10 +317,25 @@ c128 {
|
||||
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
|
||||
|
||||
|
||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
||||
|
||||
extsub $FF6E = JSRFAR()
|
||||
extsub $FF68 = SETBNK(ubyte databank @A, ubyte filenamebank @X)
|
||||
extsub $FF47 = SPIN_SPOUT() clobbers(A) ; set up serial bus for fast communications mode
|
||||
extsub $FF4A = CLOSE_ALL(ubyte device @X) clobbers(X) ; close all channels to specific device
|
||||
extsub $FF4D = C64_MODE() ; restart machine in C64 mode (does not return)
|
||||
extsub $FF50 = DMA_CALL(ubyte bank @X, ubyte command @Y) clobbers(A,X) ; send a command to a DMA device
|
||||
extsub $FF53 = BOOT_CALL(ubyte device @X, ubyte drive @A) clobbers(A,X,Y) ; try to autoboot the given disk
|
||||
extsub $FF56 = PHOENIX() clobbers(A,X,Y) ; search for and autostart ROMs, cartridges, then default disk
|
||||
extsub $FF59 = LKUPLA(ubyte lfn @A) -> bool @Pc, ubyte @X ; look up logical file number to see if it's open; returns device
|
||||
extsub $FF5C = LKUPSA(ubyte sa @Y) -> bool @Pc, ubyte @A, ubyte @X ; look up secondary address to see if it's in use; returns lfn and device
|
||||
extsub $FF5F = SWAPPER() clobbers(A,X,Y) ; swap active screen (between 40- and 80-column)
|
||||
extsub $FF62 = DLCHR() clobbers(A,X,Y) ; copy character ROM into VDC video RAM
|
||||
extsub $FF65 = PFKEY(ubyte zpaddr @A, ubyte key @X, ubyte length @Y) ; redefine programmable function key (string descriptor in zp, addr in A)
|
||||
extsub $FF68 = SETBNK(ubyte data_bank @A, ubyte filename_bank @X) ; set memory bank for load/save
|
||||
extsub $FF6B = GETCFG(ubyte bank @X) -> ubyte @A ; translate bank number to MMU configuration register value
|
||||
extsub $FF6E = JSRFAR() clobbers(A,X) ; call routine in another bank (parameters set in zero page addresses 2-8)
|
||||
extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without return to another bank (parameters set as for JSRFAR)
|
||||
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
|
||||
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
|
||||
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
|
||||
extsub $FF7D = PRIMM() ; print immediate string
|
||||
|
||||
|
||||
; ---- C128 specific system utility routines: ----
|
||||
@@ -419,12 +434,14 @@ sys {
|
||||
|
||||
const ubyte target = 128 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -445,7 +462,6 @@ sys {
|
||||
}
|
||||
|
||||
asmsub save_prog8_internals() {
|
||||
; TODO: Romable
|
||||
%asm {{
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta save_SCRATCH_ZPB1
|
||||
@@ -460,10 +476,12 @@ sys {
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -489,8 +507,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda #<_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
@@ -500,8 +518,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
pla
|
||||
@@ -516,6 +534,12 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -526,8 +550,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>cbm.IRQDFRT
|
||||
sta cbm.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #1
|
||||
sta c64.IREQMASK ; enable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
@@ -538,8 +562,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
@@ -553,8 +577,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
_raster_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
lda #$ff
|
||||
@@ -568,6 +592,12 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
@@ -650,7 +680,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -882,7 +912,7 @@ _no_msb_size
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -933,6 +963,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@@ -971,6 +1013,7 @@ cx16 {
|
||||
&uword r14 = $1bfc
|
||||
&uword r15 = $1bfe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $1be0
|
||||
&word r1s = $1be2
|
||||
&word r2s = $1be4
|
||||
@@ -988,6 +1031,7 @@ cx16 {
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
&ubyte r2L = $1be4
|
||||
@@ -1022,6 +1066,7 @@ cx16 {
|
||||
&ubyte r14H = $1bfd
|
||||
&ubyte r15H = $1bff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $1be0
|
||||
&byte r1sL = $1be2
|
||||
&byte r2sL = $1be4
|
||||
@@ -1056,8 +1101,43 @@ cx16 {
|
||||
&byte r14sH = $1bfd
|
||||
&byte r15sH = $1bff
|
||||
|
||||
; boolean versions (low and high bytes)
|
||||
&bool r0bL = $1be0
|
||||
&bool r1bL = $1be2
|
||||
&bool r2bL = $1be4
|
||||
&bool r3bL = $1be6
|
||||
&bool r4bL = $1be8
|
||||
&bool r5bL = $1bea
|
||||
&bool r6bL = $1bec
|
||||
&bool r7bL = $1bee
|
||||
&bool r8bL = $1bf0
|
||||
&bool r9bL = $1bf2
|
||||
&bool r10bL = $1bf4
|
||||
&bool r11bL = $1bf6
|
||||
&bool r12bL = $1bf8
|
||||
&bool r13bL = $1bfa
|
||||
&bool r14bL = $1bfc
|
||||
&bool r15bL = $1bfe
|
||||
|
||||
&bool r0bH = $1be1
|
||||
&bool r1bH = $1be3
|
||||
&bool r2bH = $1be5
|
||||
&bool r3bH = $1be7
|
||||
&bool r4bH = $1be9
|
||||
&bool r5bH = $1beb
|
||||
&bool r6bH = $1bed
|
||||
&bool r7bH = $1bef
|
||||
&bool r8bH = $1bf1
|
||||
&bool r9bH = $1bf3
|
||||
&bool r10bH = $1bf5
|
||||
&bool r11bH = $1bf7
|
||||
&bool r12bH = $1bf9
|
||||
&bool r13bH = $1bfb
|
||||
&bool r14bH = $1bfd
|
||||
&bool r15bH = $1bff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
; TODO: Romable
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda cx16.r0,y
|
||||
@@ -1066,9 +1146,11 @@ cx16 {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1138,16 +1220,21 @@ asmsub cleanup_at_exit() {
|
||||
sta $ff00 ; default bank 15
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,26 +16,6 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
chrout(7)
|
||||
}
|
||||
@@ -302,15 +282,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
@@ -326,14 +307,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -346,15 +328,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _colorrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
@@ -370,14 +353,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setclr._colorrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -385,25 +369,28 @@ _mod lda $ffff ; modified
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
_charptr = P8ZP_SCRATCH_W1
|
||||
_colorptr = P8ZP_SCRATCH_W2
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
sta _charptr+1
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
sta _colorptr+1
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
sta _charptr
|
||||
sta _colorptr
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
inc _charptr+1
|
||||
inc _colorptr+1
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (_charptr),y
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
sta (_colorptr),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
diskio {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
}
|
||||
@@ -4,8 +4,9 @@ FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||
.section BSS
|
||||
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
|
||||
.send BSS
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
@@ -148,6 +149,29 @@ copy_float .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
copy_float2 .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W2,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
; clobbers X
|
||||
@@ -178,10 +202,10 @@ dec_var_f .proc
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
.section BSS
|
||||
fmath_float1 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||
fmath_float2 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||
.send BSS
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
||||
|
||||
@@ -193,6 +193,7 @@ graphics {
|
||||
|
||||
if length!=0 {
|
||||
%asm {{
|
||||
_pixeladdr = P8ZP_SCRATCH_W1
|
||||
lda p8v_length
|
||||
and #7
|
||||
sta p8v_separate_pixels
|
||||
@@ -203,37 +204,35 @@ graphics {
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lda p8v_pixaddr
|
||||
sta _modified+1
|
||||
sta _pixeladdr
|
||||
lda p8v_pixaddr+1
|
||||
sta _modified+2
|
||||
sta _pixeladdr+1
|
||||
lda p8v_length
|
||||
ora p8v_length+1
|
||||
beq _zero
|
||||
ldy p8v_length
|
||||
sty P8ZP_SCRATCH_B1 ; length
|
||||
ldx #$ff
|
||||
_modified stx $ffff ; modified
|
||||
lda _modified+1
|
||||
_more txa
|
||||
ldy #0
|
||||
sta (_pixeladdr),y
|
||||
lda _pixeladdr
|
||||
clc
|
||||
adc #8
|
||||
sta _modified+1
|
||||
sta _pixeladdr
|
||||
bcc +
|
||||
inc _modified+2
|
||||
+ dey
|
||||
bne _modified
|
||||
inc _pixeladdr+1
|
||||
+ dec P8ZP_SCRATCH_B1 ; length
|
||||
bne _more
|
||||
_zero
|
||||
ldy p8v_separate_pixels
|
||||
beq hline_zero2
|
||||
lda _modified+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda _modified+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
beq _end
|
||||
lda hline_filled_right,y
|
||||
ldy #0
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
jmp hline_zero2
|
||||
ora (_pixeladdr),y
|
||||
sta (_pixeladdr),y
|
||||
_end rts
|
||||
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
||||
hline_zero2
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +376,7 @@ hline_zero2
|
||||
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
|
||||
; note: this can be even faster if we also have a 320 word x-lookup table, but hey, that's a lot of memory.
|
||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; see https://codebase64.net/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ cbm {
|
||||
|
||||
; ---- CBM ROM kernal routines (C64 addresses) ----
|
||||
|
||||
extsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
extsub $AB1E = STROUT(str strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
extsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
extsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
extsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
@@ -349,10 +349,10 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
; retrieve arguments
|
||||
ldy #$01
|
||||
lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address
|
||||
sta _jmpfar+1
|
||||
sta _jmpfar_vec
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y ; now the high byte
|
||||
sta _jmpfar+2
|
||||
sta _jmpfar_vec+1
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y ; then the target bank
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@@ -377,7 +377,7 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
plp
|
||||
jsr _jmpfar ; do the actual call
|
||||
jsr _jsrfar ; do the actual call
|
||||
; restore bank without clobbering status flags and A register
|
||||
sta P8ZP_SCRATCH_W1
|
||||
php
|
||||
@@ -390,8 +390,13 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
lda P8ZP_SCRATCH_W1
|
||||
plp
|
||||
rts
|
||||
_jsrfar jmp (_jmpfar_vec)
|
||||
|
||||
_jmpfar jmp $0000 ; modified
|
||||
.section BSS
|
||||
_jmpfar_vec .word ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -434,12 +439,14 @@ sys {
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -460,7 +467,6 @@ sys {
|
||||
}
|
||||
|
||||
asmsub save_prog8_internals() {
|
||||
; TODO: Romable
|
||||
%asm {{
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta save_SCRATCH_ZPB1
|
||||
@@ -475,10 +481,12 @@ sys {
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -504,8 +512,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda #<_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
@@ -515,8 +523,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
pla
|
||||
@@ -531,6 +539,13 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -553,8 +568,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
@@ -568,8 +583,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
_raster_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
lda #$ff
|
||||
@@ -584,6 +599,12 @@ _modified
|
||||
pla
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
@@ -666,7 +687,7 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -898,7 +919,7 @@ _no_msb_size
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -949,6 +970,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@@ -987,6 +1020,7 @@ cx16 {
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
@@ -1004,6 +1038,7 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
@@ -1038,6 +1073,7 @@ cx16 {
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
@@ -1072,8 +1108,43 @@ cx16 {
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $cfe0
|
||||
&bool r1bL = $cfe2
|
||||
&bool r2bL = $cfe4
|
||||
&bool r3bL = $cfe6
|
||||
&bool r4bL = $cfe8
|
||||
&bool r5bL = $cfea
|
||||
&bool r6bL = $cfec
|
||||
&bool r7bL = $cfee
|
||||
&bool r8bL = $cff0
|
||||
&bool r9bL = $cff2
|
||||
&bool r10bL = $cff4
|
||||
&bool r11bL = $cff6
|
||||
&bool r12bL = $cff8
|
||||
&bool r13bL = $cffa
|
||||
&bool r14bL = $cffc
|
||||
&bool r15bL = $cffe
|
||||
|
||||
&bool r0bH = $cfe1
|
||||
&bool r1bH = $cfe3
|
||||
&bool r2bH = $cfe5
|
||||
&bool r3bH = $cfe7
|
||||
&bool r4bH = $cfe9
|
||||
&bool r5bH = $cfeb
|
||||
&bool r6bH = $cfed
|
||||
&bool r7bH = $cfef
|
||||
&bool r8bH = $cff1
|
||||
&bool r9bH = $cff3
|
||||
&bool r10bH = $cff5
|
||||
&bool r11bH = $cff7
|
||||
&bool r12bH = $cff9
|
||||
&bool r13bH = $cffb
|
||||
&bool r14bH = $cffd
|
||||
&bool r15bH = $cfff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
; TODO: Romable
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda cx16.r0,y
|
||||
@@ -1082,9 +1153,11 @@ cx16 {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1170,16 +1243,21 @@ asmsub cleanup_at_exit() {
|
||||
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,25 +15,6 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
|
||||
|
||||
sub clear_screen() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
; beep
|
||||
@@ -305,15 +286,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
@@ -329,14 +311,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -349,15 +332,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _colorrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
@@ -373,14 +357,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setclr._colorrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -388,25 +373,28 @@ _mod lda $ffff ; modified
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
_charptr = P8ZP_SCRATCH_W1
|
||||
_colorptr = P8ZP_SCRATCH_W2
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
sta _charptr+1
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
sta _colorptr+1
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
sta _charptr
|
||||
sta _colorptr
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
inc _charptr+1
|
||||
inc _colorptr+1
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (_charptr),y
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
sta (_colorptr),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ conv {
|
||||
|
||||
; ----- number conversions to decimal strings ----
|
||||
|
||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
ubyte[16] @shared string_out ; result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
|
||||
|
||||
asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
|
||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||
@@ -352,7 +352,6 @@ asmsub str2word(str string @AY) -> word @AY {
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
; TODO: Romable
|
||||
%asm {{
|
||||
_result = P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
@@ -403,7 +402,9 @@ _digit
|
||||
+ iny
|
||||
bne _parse
|
||||
; never reached
|
||||
_negative .byte 0
|
||||
.section BSS
|
||||
_negative .byte ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -540,7 +541,6 @@ asmsub internal_uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @
|
||||
; output in internal_uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
||||
; (these are terminated by a zero byte so they can be easily printed)
|
||||
; also returns Y = 100's, A = 10's, X = 1's
|
||||
; TODO: Romable
|
||||
|
||||
%asm {{
|
||||
|
||||
@@ -701,12 +701,14 @@ ShiftedBcdTab
|
||||
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||
|
||||
decTenThousands .byte 0
|
||||
decThousands .byte 0
|
||||
decHundreds .byte 0
|
||||
decTens .byte 0
|
||||
decOnes .byte 0
|
||||
.byte 0 ; zero-terminate the decimal output string
|
||||
.section BSS
|
||||
decTenThousands .byte ?
|
||||
decThousands .byte ?
|
||||
decHundreds .byte ?
|
||||
decTens .byte ?
|
||||
decOnes .byte ?
|
||||
.byte ? ; zero-terminate the decimal output string, set to 0 by bss init mechanisms
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -758,7 +760,9 @@ asmsub internal_uword2hex (uword value @AY) clobbers(A,Y) {
|
||||
sta output+2
|
||||
sty output+3
|
||||
rts
|
||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||
.section BSS
|
||||
output .fill 5 ; 0-terminated output buffer (to make printing easier)
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,20 @@
|
||||
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
|
||||
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
|
||||
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
||||
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
|
||||
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
|
||||
; - call current() to get the current task id.
|
||||
; - call kill(taskid) to kill a task by id.
|
||||
; - call killall() to kill all tasks.
|
||||
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
||||
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
||||
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
|
||||
;
|
||||
; TIP: HOW TO WAIT without BLOCKING other coroutines?
|
||||
; Make sure you call yield() in the waiting loop, for example:
|
||||
; uword timer = cbm.RDTIM16() + 60
|
||||
; while cbm.RDTIM16() != timer
|
||||
; void coroutines.yield()
|
||||
|
||||
coroutines {
|
||||
%option ignore_unused
|
||||
@@ -41,19 +49,17 @@ coroutines {
|
||||
const ubyte MAX_TASKS = 64
|
||||
uword[MAX_TASKS] tasklist
|
||||
uword[MAX_TASKS] userdatas
|
||||
uword[MAX_TASKS] returnaddresses
|
||||
ubyte active_task
|
||||
uword supervisor
|
||||
|
||||
sub add(uword taskaddress, uword userdata) -> ubyte {
|
||||
sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
|
||||
; find the next empty slot in the tasklist and stick it there
|
||||
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
|
||||
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
if tasklist[cx16.r0L] == 0 {
|
||||
tasklist[cx16.r0L] = taskaddress
|
||||
tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
|
||||
userdatas[cx16.r0L] = userdata
|
||||
returnaddresses[cx16.r0L] = 0
|
||||
sys.set_carry()
|
||||
return cx16.r0L
|
||||
}
|
||||
@@ -70,57 +76,48 @@ coroutines {
|
||||
}
|
||||
}
|
||||
|
||||
sub run(uword supervisor_routine) {
|
||||
sub run(uword @nozp supervisor_routine) {
|
||||
supervisor = supervisor_routine
|
||||
for active_task in 0 to len(tasklist)-1 {
|
||||
if tasklist[active_task]!=0 {
|
||||
; activate the termination handler and start the first task
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto tasklist[active_task]
|
||||
sys.pushw(tasklist[active_task])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub yield() -> uword {
|
||||
; Store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value
|
||||
uword task_start, task_continue
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
; Store the return address of the yielding task, and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value.
|
||||
; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
|
||||
uword task_return_address
|
||||
tasklist[active_task] = sys.popw()
|
||||
|
||||
resume_with_next_task:
|
||||
skip_task:
|
||||
if not next_task() {
|
||||
void sys.popw() ; remove return to the termination handler
|
||||
return 0 ; exiting here will now actually return from the start() call back to the calling program :)
|
||||
return 0 ; exiting here will now actually return back to the calling program that called run()
|
||||
}
|
||||
|
||||
if supervisor!=0 {
|
||||
if supervisor!=0
|
||||
if lsb(call(supervisor))==0
|
||||
goto resume_with_next_task
|
||||
}
|
||||
goto skip_task
|
||||
|
||||
if task_continue==0 {
|
||||
; fetch start address of next task.
|
||||
; address on the stack must be pushed in reverse byte order
|
||||
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(task_start)
|
||||
} else
|
||||
sys.pushw(task_continue)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
; returning from yield then continues with the next coroutine:
|
||||
sys.pushw(task_return_address)
|
||||
return userdatas[active_task]
|
||||
|
||||
sub next_task() -> bool {
|
||||
; search through the task list for the next active task
|
||||
repeat len(tasklist) {
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
if active_task==len(tasklist)
|
||||
active_task=0
|
||||
task_start = tasklist[active_task]
|
||||
if task_start!=0 {
|
||||
task_continue = returnaddresses[active_task]
|
||||
task_return_address = tasklist[active_task]
|
||||
if task_return_address!=0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -128,9 +125,8 @@ resume_with_next_task:
|
||||
}
|
||||
}
|
||||
|
||||
sub kill(ubyte taskid) {
|
||||
sub kill(ubyte @nozp taskid) {
|
||||
tasklist[taskid] = 0
|
||||
returnaddresses[taskid] = 0
|
||||
}
|
||||
|
||||
sub current() -> ubyte {
|
||||
@@ -138,12 +134,10 @@ resume_with_next_task:
|
||||
}
|
||||
|
||||
sub termination() {
|
||||
; a task has terminated. wipe it from the list.
|
||||
; this is an internal routine
|
||||
; internal routine: a task has terminated. wipe it from the list.
|
||||
kill(active_task)
|
||||
; reactivate this termination handler
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
; reactivate this termination handler and go to the next task
|
||||
sys.push_returnaddress(&termination)
|
||||
goto coroutines.yield.resume_with_next_task
|
||||
goto coroutines.yield.skip_task
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@ bmx {
|
||||
uword palette_entries ; 1-256
|
||||
ubyte palette_start
|
||||
ubyte compression
|
||||
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
^^ubyte @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
|
||||
uword error_message ; pointer to error message, or 0 if all ok
|
||||
^^ubyte error_message ; pointer to error message, or 0 if all ok
|
||||
ubyte old_drivenumber
|
||||
|
||||
sub open(ubyte drivenumber, str filename) -> bool {
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
; NOTE: If you experience weird behavior with these routines and you are using them
|
||||
; in the X16 emulator using HostFs, please try again with an SD-card image instead first.
|
||||
; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator.
|
||||
;
|
||||
|
||||
; About the secondary addresses:
|
||||
; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string)
|
||||
; for reads (existing files) you can use 0 or 2-14 (mode string is optional)
|
||||
; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required
|
||||
|
||||
; About the Dos/Drive error status message:
|
||||
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
|
||||
; In case of an error, you usually have to read/clear that yourself (with status() for example).
|
||||
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
@@ -21,6 +25,7 @@ diskio {
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
const ubyte STATUS_EOF=$40
|
||||
|
||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
|
||||
@@ -94,7 +99,7 @@ io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
||||
if status!=0 and status & STATUS_EOF == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
@@ -116,7 +121,7 @@ io_error:
|
||||
goto diskio.directory.internal_dir
|
||||
}
|
||||
|
||||
sub diskname() -> uword {
|
||||
sub diskname() -> str {
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -160,7 +165,7 @@ io_error:
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
bool list_skip_disk_name
|
||||
uword list_pattern
|
||||
^^ubyte list_pattern
|
||||
uword list_blocks
|
||||
bool iteration_in_progress = false
|
||||
bool write_iteration_in_progress = false
|
||||
@@ -169,7 +174,7 @@ io_error:
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
@@ -204,7 +209,7 @@ io_error:
|
||||
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list(str pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(1, "$")
|
||||
@@ -234,7 +239,7 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_start_list_dirs(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_dirs(str pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version it only returns directory entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -242,7 +247,7 @@ io_error:
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
sub lf_start_list_files(uword pattern_ptr) -> bool {
|
||||
sub lf_start_list_files(str pattern_ptr) -> bool {
|
||||
; -- start an iterative directory contents listing with optional pattern matching.
|
||||
; this version only returns actual file entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
@@ -262,7 +267,7 @@ io_error:
|
||||
repeat {
|
||||
reset_read_channel() ; use the input io channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
^^ubyte nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
@@ -346,7 +351,7 @@ close_end:
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if cbm.READST()==0 {
|
||||
if cbm.READST() & ~STATUS_EOF == 0 {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
@@ -375,7 +380,7 @@ close_end:
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L!=0 {
|
||||
f_close()
|
||||
if cx16.r0L & $40 == 0
|
||||
if cx16.r0L & STATUS_EOF == 0
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
@@ -395,7 +400,7 @@ close_end:
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= readsize
|
||||
if cbm.READST() & $40 !=0 {
|
||||
if cbm.READST() & STATUS_EOF !=0 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
@@ -406,39 +411,39 @@ close_end:
|
||||
byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
}}
|
||||
while num_bytes!=0 {
|
||||
%asm {{
|
||||
jsr cbm.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
sta (P8ZP_SCRATCH_W1)
|
||||
inc P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+
|
||||
}}
|
||||
list_blocks++
|
||||
cx16.r0L = cbm.READST()
|
||||
if_nz {
|
||||
f_close()
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L & $40 !=0 ; eof?
|
||||
if cx16.r0L & STATUS_EOF !=0 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; -- read the full rest of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||
; Usually you will just be using load() / load_raw() or vload() / vload_raw() to read entire files!
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
@@ -446,6 +451,8 @@ m_in_buffer sta $ffff
|
||||
uword total_read = 0
|
||||
while cbm.READST()==0 {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
if cx16.r0==0
|
||||
break
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
}
|
||||
@@ -453,7 +460,7 @@ m_in_buffer sta $ffff
|
||||
return total_read
|
||||
}
|
||||
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
asmsub f_readline(^^ubyte bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
@@ -583,7 +590,7 @@ return_status:
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status() -> uword {
|
||||
sub status() -> str {
|
||||
; -- retrieve the disk drive's current status message
|
||||
|
||||
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
|
||||
@@ -594,7 +601,7 @@ return_status:
|
||||
; return device_not_present_error
|
||||
; }
|
||||
|
||||
uword messageptr = &list_filename
|
||||
^^ubyte messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN() ; open 15,8,15
|
||||
@@ -656,16 +663,16 @@ io_error:
|
||||
|
||||
|
||||
; saves a block of memory to disk, including the default 2 byte prg header.
|
||||
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save(str filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, false)
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
sub save_raw(str filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
sub internal_save_routine(str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = startaddress + savesize
|
||||
@@ -712,19 +719,25 @@ io_error:
|
||||
; the return address will still be one higher. Which means, because the Kernal
|
||||
; load routine is bank-aware, it will return $a000 and will have switched to the next hiram bank!
|
||||
; So you'll have to reset the ram bank with cx16.rambank() to switch back to the bank that the data was put in.
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
sub load(str filenameptr, uword address_override) -> uword {
|
||||
return internal_load_routine(filenameptr, address_override, false)
|
||||
}
|
||||
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details. Including the banking behavior on the X16.
|
||||
sub load_raw(uword filenameptr, uword startaddress) -> uword {
|
||||
sub load_raw(str filenameptr, uword startaddress) -> uword {
|
||||
return internal_load_routine(filenameptr, startaddress, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||
sub loadlib(str libnameptr, uword libaddress) -> uword {
|
||||
return internal_load_routine(libnameptr, libaddress, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(str filenameptr, uword address_override, bool headerless) -> uword {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
@@ -747,7 +760,7 @@ io_error:
|
||||
return cx16.r1
|
||||
}
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
sub delete(str filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
list_filename[1] = ':'
|
||||
@@ -759,7 +772,7 @@ io_error:
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
sub rename(str oldfileptr, str newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = ':'
|
||||
@@ -773,7 +786,7 @@ io_error:
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub send_command(uword commandptr) {
|
||||
sub send_command(str commandptr) {
|
||||
; -- send a dos command to the drive (don't read any response)
|
||||
cbm.SETNAM(strings.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
@@ -893,7 +906,7 @@ internal_vload:
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub curdir() -> uword {
|
||||
sub curdir() -> str {
|
||||
; return current directory name or 0 if error
|
||||
; special X16 dos command to only return the current path in the entry list (R42+)
|
||||
const ubyte MAX_PATH_LEN=80
|
||||
@@ -941,7 +954,7 @@ internal_vload:
|
||||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status!=0 and status & $40 == 0
|
||||
if status!=0 and status & STATUS_EOF == 0
|
||||
return 0
|
||||
if @(cx16.r12)==0 {
|
||||
cx16.r12--
|
||||
@@ -973,6 +986,7 @@ io_error:
|
||||
sub exists(str filename) -> bool {
|
||||
; -- returns true if the given file exists on the disk, otherwise false
|
||||
; DON'T use this if you already have a file open with f_open!
|
||||
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
|
||||
if f_open(filename) {
|
||||
f_close()
|
||||
return true
|
||||
|
||||
@@ -47,6 +47,12 @@ emudbg {
|
||||
}
|
||||
}
|
||||
|
||||
sub console_nl() {
|
||||
; write a newline to the debug output console.
|
||||
; because '\n' gets encoded in the x16 value for it (13) which is different than what the shell expects (10).
|
||||
console_chrout(10)
|
||||
}
|
||||
|
||||
sub console_chrout(ubyte char) {
|
||||
; note: make sure the character is in Iso encoding.
|
||||
if is_emulator()
|
||||
|
||||
@@ -21,8 +21,8 @@ extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo'
|
||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
extsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
extsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
extsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
|
||||
extsub $fe06 = FOUT() clobbers(X) -> str @ AY ; fac1 -> string, address returned in AY
|
||||
extsub $fe09 = VAL_1(str string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
|
||||
|
||||
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
|
||||
@@ -154,9 +154,9 @@ gfx_hires {
|
||||
inc cx16.VERA_ADDR_L
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_M
|
||||
+ bne +
|
||||
+ bne +
|
||||
inc cx16.VERA_ADDR_H
|
||||
+ inx ; next pixel
|
||||
+ inx ; next pixel
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -825,7 +825,7 @@ set_byte:
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
}
|
||||
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
|
||||
@@ -743,7 +743,7 @@ skip:
|
||||
const ubyte charset_bank = $1
|
||||
const uword charset_addr = $f000 ; in bank 1, so $1f000
|
||||
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword textptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, str textptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in an encoding approprite for the charset.
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
|
||||
Loaded 100 of 337 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user