mirror of
https://github.com/irmen/prog8.git
synced 2025-06-11 04:38:06 +00:00
Compare commits
273 Commits
Author | SHA1 | Date | |
---|---|---|---|
daf7c3357c | |||
e8795859c5 | |||
bebe60b687 | |||
ddceec364e | |||
d067fa4b73 | |||
b5e51ab937 | |||
552e55c29f | |||
a228908c1a | |||
15fc3b6c04 | |||
0456badd02 | |||
399cf5118d | |||
a87f2640d3 | |||
a90ef274d7 | |||
341778ba67 | |||
ec50b5a007 | |||
31d84c8921 | |||
34bedbeef1 | |||
3b1b0985c1 | |||
368387e1a7 | |||
9da430ffeb | |||
cc063124cf | |||
3b37b89951 | |||
844b537d1e | |||
caf1d4a22a | |||
d8e244df99 | |||
548e421e27 | |||
322fa7ea69 | |||
cf7bea0985 | |||
25d7f8808f | |||
acc630972a | |||
6a33be3fd8 | |||
f5fc4e345c | |||
67231af623 | |||
e31ef6f06f | |||
09d188106a | |||
d8e2116481 | |||
435dfbb932 | |||
ba93966474 | |||
ea8d17cdb2 | |||
082265fb25 | |||
9e557ce8ac | |||
e5d9af75de | |||
31c1bf8bc5 | |||
37d4055036 | |||
78b1076110 | |||
0a3c748e41 | |||
ebf79ef9e2 | |||
99b9370178 | |||
d634061cd9 | |||
d59d8ff1fe | |||
53e442d509 | |||
f7cbfdff06 | |||
b28ee0819f | |||
522958e0e9 | |||
ccc6b56e35 | |||
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 | |||
8f8d99e3ed | |||
23474360ec | |||
81c255c450 | |||
ef23d52ed7 | |||
220ab773aa | |||
e3e5bff7bb | |||
7b9a841b2a | |||
40423911ef | |||
582a70b046 | |||
5b63590ebf | |||
2b6510dc19 | |||
9d49589d73 | |||
125b66c929 | |||
5255f1c052 | |||
a6ba05d60c | |||
41e963b04b | |||
6ff75bef29 | |||
72c16d0d32 | |||
94653e5c8c | |||
3e2b2a698d | |||
ae04f5aee8 | |||
5c56267662 | |||
e55ce5504e | |||
fb1e89d9ef | |||
bc550a4549 | |||
ebdea9cf76 | |||
09ec508f82 | |||
d06e9ea7f6 | |||
a36bdc54fd | |||
0814ea9711 | |||
daefe839d8 | |||
e6088dd315 | |||
fc03d6f332 | |||
2aeb7a838e | |||
99ff5dd078 | |||
49982b49b6 | |||
fd39c22616 | |||
9e79722a7f | |||
17334a1c58 | |||
c7f0ff11ac | |||
cd2cc89e6a | |||
069143092d | |||
efd41260f2 | |||
8d2410622c | |||
60554389b3 | |||
a940dc7d43 | |||
06ca68a625 | |||
5b58e5b158 | |||
74dd8fe80b | |||
75ddcda5f3 | |||
216825b98a | |||
a96defab86 | |||
0864b0a1b7 | |||
8b158d9240 | |||
f335251c2b | |||
67bc0b6931 | |||
e646dd1ed1 | |||
2b7947f9b0 | |||
ec0cfb4b3f | |||
9cdf53019c | |||
1a04a3eb3a | |||
105d3995e0 | |||
8ce3204f93 | |||
d0f15f1285 | |||
66d6f67120 | |||
a106c88054 | |||
ee784e1ccc | |||
bb75be0b44 | |||
2478aea316 | |||
1e17df5296 | |||
8583a96519 | |||
d0c184c7de | |||
0191acb2b3 | |||
277a1a32b2 | |||
7a13f57ab0 | |||
0c882836d9 | |||
228be5cd04 | |||
08cd2fd6e8 | |||
bc7b086f0f | |||
e8f3af6981 | |||
f9c7c7dab7 | |||
09a17743ad | |||
4f096a7511 | |||
2ab2130000 | |||
66558f7638 | |||
a6f9ed07e7 | |||
7268a8736f | |||
8f6b5676d7 | |||
ca9422bbe9 | |||
35d9412559 | |||
f071c07dd9 | |||
e5ff3c1ff3 | |||
f0e8ff0326 | |||
3b5cda85ff | |||
420793f9e2 | |||
cf1dbaf0d8 | |||
d187cef6b7 | |||
3b3616afda | |||
0ffebc25d0 | |||
478e2b4ebd | |||
a56ae7539a | |||
407773bda2 | |||
823eaa8918 | |||
a2be42c5ca | |||
a76b8d66ff | |||
b7f47d354f | |||
5d33c93af9 | |||
4db6859f3f | |||
45fe1bb16e | |||
b014facbd3 | |||
3b4b37f16b | |||
68d5983a14 | |||
f2cfcfdf31 | |||
10b9162dc5 | |||
c84cc8f8c9 | |||
78c71bbf0e | |||
37c2c1bf0b | |||
c8996418da | |||
76b29aa629 | |||
ee521793f8 | |||
f42e12bc13 | |||
427451a23f | |||
af7930d494 | |||
e2882d37bf | |||
942d3ee640 | |||
7b4a82b91a | |||
056c0a24d9 | |||
827df04b32 | |||
e174b31344 | |||
49959af752 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
#patreon: # Replace with a single Patreon username
|
#patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
#open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: irmen
|
ko_fi: irmen
|
||||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
@ -11,3 +11,4 @@ ko_fi: irmen
|
|||||||
#otechie: # Replace with a single Otechie username
|
#otechie: # Replace with a single Otechie username
|
||||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
custom: ['https://paypal.me/irmendejong']
|
||||||
|
1458
.idea/inspectionProfiles/Project_Default.xml
generated
1458
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -14,6 +14,6 @@
|
|||||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.1.0" />
|
<option name="version" value="2.1.10" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
20
.idea/libraries/KotlinJavaRuntime.xml
generated
20
.idea/libraries/KotlinJavaRuntime.xml
generated
@ -1,23 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="KotlinJavaRuntime" type="repository">
|
<library name="KotlinJavaRuntime" type="repository">
|
||||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0" />
|
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.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/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.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/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
10
.idea/libraries/eclipse_lsp4j.xml
generated
10
.idea/libraries/eclipse_lsp4j.xml
generated
@ -1,11 +1,11 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="eclipse.lsp4j" type="repository">
|
<library name="eclipse.lsp4j" type="repository">
|
||||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" />
|
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
|
<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.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.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.11.0/gson-2.11.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.27.0/error_prone_annotations-2.27.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0.jar!/" />
|
<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$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-javadoc.jar!/" />
|
<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$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-sources.jar!/" />
|
<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$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
|
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@ -12,6 +12,7 @@
|
|||||||
<option name="pkg" value="" />
|
<option name="pkg" value="" />
|
||||||
<option name="language" value="Java" />
|
<option name="language" value="Java" />
|
||||||
<option name="generateListener" value="false" />
|
<option name="generateListener" value="false" />
|
||||||
|
<option name="generateVisitor" value="true" />
|
||||||
</PerGrammarGenerationSettings>
|
</PerGrammarGenerationSettings>
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
@ -22,7 +23,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<type id="Python" />
|
<type id="Python" />
|
||||||
</component>
|
</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" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -17,6 +17,7 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
|
14
README.md
14
README.md
@ -1,4 +1,7 @@
|
|||||||
[](https://ko-fi.com/H2H6S0FFF)
|
[](https://ko-fi.com/H2H6S0FFF)
|
||||||
|
|
||||||
|
PayPal: [](https://paypal.me/irmendejong)
|
||||||
|
|
||||||
[](https://prog8.readthedocs.io/)
|
[](https://prog8.readthedocs.io/)
|
||||||
|
|
||||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||||
@ -14,7 +17,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
|||||||
|
|
||||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
@ -60,16 +63,20 @@ What does Prog8 provide?
|
|||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- floating point math is supported on certain targets
|
- floating point math is supported on certain targets
|
||||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
- 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.
|
- 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
|
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||||
- high-level program optimizations
|
- high-level program optimizations
|
||||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
|
||||||
- conditional branches that map 1:1 to cpu status flags
|
- conditional branches that map 1:1 to cpu status flags
|
||||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||||
|
- ``on .. goto`` statement for fast jump tables
|
||||||
- ``in`` expression for concise and efficient multi-value/containment check
|
- ``in`` expression for concise and efficient multi-value/containment check
|
||||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||||
|
- subroutines can return more than one result value
|
||||||
- inline assembly allows you to have full control when every cycle or byte matters
|
- inline assembly allows you to have full control when every cycle or byte matters
|
||||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||||
- encode strings and characters into petscii or screencodes or even other encodings
|
- encode strings and characters into petscii or screencodes or even other encodings
|
||||||
@ -89,8 +96,7 @@ What does Prog8 provide?
|
|||||||
- "c64": Commodore-64 (6502 like CPU)
|
- "c64": Commodore-64 (6502 like CPU)
|
||||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||||
- "pet32": Commodore PET (limited support)
|
- "pet32": Commodore PET (limited support)
|
||||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||||
- "neo": Neo6502 (experimental)
|
|
||||||
- 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)
|
- 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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ circles {
|
|||||||
|
|
||||||
sub draw(bool use_kernal, uword max_time) -> uword {
|
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||||
if use_kernal
|
if use_kernal
|
||||||
void cx16.set_screen_mode(128)
|
cx16.set_screen_mode(128)
|
||||||
else
|
else
|
||||||
gfx_lores.graphics_mode()
|
gfx_lores.graphics_mode()
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ circles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if use_kernal
|
if use_kernal
|
||||||
void cx16.set_screen_mode(3)
|
cx16.set_screen_mode(3)
|
||||||
else {
|
else {
|
||||||
gfx_lores.text_mode()
|
gfx_lores.text_mode()
|
||||||
}
|
}
|
||||||
|
69
benchmark-program/b_sprites.p8
Normal file
69
benchmark-program/b_sprites.p8
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
%import sprites
|
||||||
|
%import coroutines
|
||||||
|
%import math
|
||||||
|
|
||||||
|
|
||||||
|
animsprites {
|
||||||
|
uword num_iterations
|
||||||
|
ubyte[64] sx
|
||||||
|
ubyte[64] sy
|
||||||
|
ubyte[64] sc
|
||||||
|
ubyte[64] dx
|
||||||
|
ubyte[64] dy
|
||||||
|
uword maximum_duration
|
||||||
|
|
||||||
|
sub benchmark(uword max_duration) -> uword {
|
||||||
|
maximum_duration = max_duration
|
||||||
|
math.rndseed(1122,9876)
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
cx16.mouse_config2(1)
|
||||||
|
sprites.set_mousepointer_hand()
|
||||||
|
repeat 64
|
||||||
|
void coroutines.add(animsprite, 0)
|
||||||
|
cx16.mouse_config2(0)
|
||||||
|
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
coroutines.run(supervisor)
|
||||||
|
|
||||||
|
sprites.reset(0, 64)
|
||||||
|
return num_iterations
|
||||||
|
}
|
||||||
|
|
||||||
|
sub supervisor() -> bool {
|
||||||
|
if cbm.RDTIM16() >= maximum_duration {
|
||||||
|
coroutines.killall()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub animsprite() {
|
||||||
|
num_iterations++
|
||||||
|
; set up the sprite
|
||||||
|
ubyte sprnum = coroutines.current()
|
||||||
|
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||||
|
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||||
|
sx[sprnum] = math.rnd()
|
||||||
|
sy[sprnum] = math.rnd()
|
||||||
|
sc[sprnum] = math.rnd()
|
||||||
|
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||||
|
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||||
|
|
||||||
|
; move the sprite around
|
||||||
|
while sc[sprnum]!=0 {
|
||||||
|
animate(sprnum)
|
||||||
|
void coroutines.yield()
|
||||||
|
sprnum = coroutines.current()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub animate(ubyte spr) {
|
||||||
|
defer sc[spr]--
|
||||||
|
sprites.pos(spr, sx[spr], sy[spr])
|
||||||
|
sx[spr] += dx[spr]
|
||||||
|
sy[spr] += dy[spr]
|
||||||
|
}
|
||||||
|
|
||||||
|
; end the task but replace it with a fresh animated sprite task
|
||||||
|
void coroutines.add(animsprite, 0)
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
%import b_queens
|
%import b_queens
|
||||||
%import b_textelite
|
%import b_textelite
|
||||||
%import b_maze
|
%import b_maze
|
||||||
|
%import b_sprites
|
||||||
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
@ -29,7 +30,7 @@ main {
|
|||||||
sub start() {
|
sub start() {
|
||||||
ubyte benchmark_number
|
ubyte benchmark_number
|
||||||
|
|
||||||
void cx16.set_screen_mode(3)
|
cx16.set_screen_mode(3)
|
||||||
txt.color2(1, 6)
|
txt.color2(1, 6)
|
||||||
txt.clear_screen()
|
txt.clear_screen()
|
||||||
|
|
||||||
@ -74,10 +75,14 @@ main {
|
|||||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||||
benchmark_number++
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("sprites-coroutines-defer")
|
||||||
|
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
benchmark_names[benchmark_number] = 0
|
benchmark_names[benchmark_number] = 0
|
||||||
benchmark_score[benchmark_number] = 0
|
benchmark_score[benchmark_number] = 0
|
||||||
|
|
||||||
void cx16.set_screen_mode(3)
|
cx16.set_screen_mode(3)
|
||||||
txt.uppercase()
|
txt.uppercase()
|
||||||
txt.color2(1, 6)
|
txt.color2(1, 6)
|
||||||
uword final_score
|
uword final_score
|
||||||
@ -99,7 +104,7 @@ main {
|
|||||||
|
|
||||||
sub announce_benchmark(str name) {
|
sub announce_benchmark(str name) {
|
||||||
benchmark_names[benchmark_number] = name
|
benchmark_names[benchmark_number] = name
|
||||||
void cx16.set_screen_mode(3)
|
cx16.set_screen_mode(3)
|
||||||
txt.uppercase()
|
txt.uppercase()
|
||||||
txt.color2(1, 6)
|
txt.color2(1, 6)
|
||||||
txt.clear_screen()
|
txt.clear_screen()
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||||
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.1.0"
|
kotlin("jvm") version "2.1.20"
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -17,6 +19,12 @@ allprojects {
|
|||||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||||
jvmTarget = JvmTarget.JVM_11
|
jvmTarget = JvmTarget.JVM_11
|
||||||
}
|
}
|
||||||
|
sourceSets.all {
|
||||||
|
languageSettings {
|
||||||
|
// enable language features like so:
|
||||||
|
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -7,7 +7,7 @@ plugins {
|
|||||||
dependencies {
|
dependencies {
|
||||||
// should have no dependencies to other modules
|
// should have no dependencies to other modules
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -21,4 +21,4 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
// note: there are no unit tests in this module!
|
||||||
|
31
codeCore/src/prog8/code/Globals.kt
Normal file
31
codeCore/src/prog8/code/Globals.kt
Normal file
@ -0,0 +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) }
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,6 @@ class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
|
|||||||
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||||
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||||
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
|
||||||
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
|
||||||
|
|
||||||
|
|
||||||
class FSignature(val pure: Boolean, // does it have side effects?
|
class FSignature(val pure: Boolean, // does it have side effects?
|
||||||
@ -69,7 +67,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
|||||||
}
|
}
|
||||||
CallConvention(listOf(paramConv), returns)
|
CallConvention(listOf(paramConv), returns)
|
||||||
}
|
}
|
||||||
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
|
actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> {
|
||||||
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
|
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
|
||||||
}
|
}
|
||||||
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
|
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
|
||||||
@ -98,11 +96,9 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
"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_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_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||||
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
|
|
||||||
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
|
|
||||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||||
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
|
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||||
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),
|
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
"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.toTypedArray())),
|
||||||
|
@ -11,6 +11,7 @@ class CompilationOptions(val output: OutputType,
|
|||||||
val zpAllowed: List<UIntRange>,
|
val zpAllowed: List<UIntRange>,
|
||||||
val floats: Boolean,
|
val floats: Boolean,
|
||||||
val noSysInit: Boolean,
|
val noSysInit: Boolean,
|
||||||
|
val romable: Boolean,
|
||||||
val compTarget: ICompilationTarget,
|
val compTarget: ICompilationTarget,
|
||||||
// these are set later, based on command line arguments or options in the source code:
|
// these are set later, based on command line arguments or options in the source code:
|
||||||
var loadAddress: UInt,
|
var loadAddress: UInt,
|
||||||
@ -31,10 +32,11 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var breakpointCpuInstruction: String? = null,
|
var breakpointCpuInstruction: String? = null,
|
||||||
var ignoreFootguns: Boolean = false,
|
var ignoreFootguns: Boolean = false,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
|
var quiet: Boolean = false,
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
compTarget.machine.initializeMemoryAreas(this)
|
compTarget.initializeMemoryAreas(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
import java.util.Objects
|
import java.util.*
|
||||||
|
|
||||||
enum class BaseDataType {
|
enum class BaseDataType {
|
||||||
UBYTE, // pass by value 8 bits unsigned
|
UBYTE, // pass by value 8 bits unsigned
|
||||||
@ -23,6 +23,7 @@ enum class BaseDataType {
|
|||||||
this.isWord -> other.isByteOrBool
|
this.isWord -> other.isByteOrBool
|
||||||
this == LONG -> other.isByteOrBool || other.isWord
|
this == LONG -> other.isByteOrBool || other.isWord
|
||||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||||
|
this.isArray && other.isArray -> false
|
||||||
this.isArray -> other != FLOAT
|
this.isArray -> other != FLOAT
|
||||||
this == STR -> other != FLOAT
|
this == STR -> other != FLOAT
|
||||||
else -> true
|
else -> true
|
||||||
@ -31,6 +32,7 @@ enum class BaseDataType {
|
|||||||
fun equalsSize(other: BaseDataType) =
|
fun equalsSize(other: BaseDataType) =
|
||||||
when {
|
when {
|
||||||
this == other -> true
|
this == other -> true
|
||||||
|
this.isArray && other.isArray -> true
|
||||||
this.isByteOrBool -> other.isByteOrBool
|
this.isByteOrBool -> other.isByteOrBool
|
||||||
this.isWord -> other.isWord
|
this.isWord -> other.isWord
|
||||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||||
@ -78,6 +80,17 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
|||||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||||
|
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||||
|
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||||
|
val WORD = DataType(BaseDataType.WORD, null)
|
||||||
|
val LONG = DataType(BaseDataType.LONG, null)
|
||||||
|
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||||
|
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||||
|
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||||
|
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||||
|
|
||||||
private val simpletypes = mapOf(
|
private val simpletypes = mapOf(
|
||||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||||
@ -100,7 +113,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
|||||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||||
else
|
else
|
||||||
throw NoSuchElementException("invalid element dt "+elementDt)
|
throw NoSuchElementException("invalid element dt $elementDt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,14 +196,8 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
|||||||
BaseDataType.UNDEFINED -> false
|
BaseDataType.UNDEFINED -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun largerSizeThan(other: DataType): Boolean {
|
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||||
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
|
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||||
return base.largerSizeThan(other.base)
|
|
||||||
}
|
|
||||||
fun equalsSize(other: DataType): Boolean {
|
|
||||||
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
|
|
||||||
return base.equalsSize(other.base)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isUndefined = base == BaseDataType.UNDEFINED
|
val isUndefined = base == BaseDataType.UNDEFINED
|
||||||
val isByte = base.isByte
|
val isByte = base.isByte
|
||||||
@ -321,7 +328,8 @@ val CpuRegisters = arrayOf(
|
|||||||
enum class OutputType {
|
enum class OutputType {
|
||||||
RAW,
|
RAW,
|
||||||
PRG,
|
PRG,
|
||||||
XEX
|
XEX,
|
||||||
|
LIBRARY
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CbmPrgLauncherType {
|
enum class CbmPrgLauncherType {
|
||||||
|
@ -1,8 +1,43 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
enum class CpuType {
|
||||||
|
CPU6502,
|
||||||
|
CPU65C02,
|
||||||
|
VIRTUAL
|
||||||
|
}
|
||||||
|
|
||||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||||
val name: String
|
val name: String
|
||||||
val machine: IMachineDefinition
|
|
||||||
|
val FLOAT_MAX_NEGATIVE: Double
|
||||||
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
|
val FLOAT_MEM_SIZE: Int
|
||||||
|
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||||
|
val PROGRAM_LOAD_ADDRESS : UInt
|
||||||
|
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||||
|
val BSSHIGHRAM_START: UInt
|
||||||
|
val BSSHIGHRAM_END: UInt
|
||||||
|
val BSSGOLDENRAM_START: UInt
|
||||||
|
val BSSGOLDENRAM_END: UInt
|
||||||
|
|
||||||
|
val cpu: CpuType
|
||||||
|
var zeropage: Zeropage
|
||||||
|
var golden: GoldenRam
|
||||||
|
val libraryPath: Path?
|
||||||
|
val customLauncher: List<String>
|
||||||
|
val additionalAssemblerOptions: String?
|
||||||
|
val defaultOutputType: OutputType
|
||||||
|
|
||||||
|
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||||
|
fun getFloatAsmBytes(num: Number): String
|
||||||
|
|
||||||
|
fun convertFloatToBytes(num: Double): List<UByte>
|
||||||
|
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||||
|
|
||||||
|
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||||
|
fun isIOAddress(address: UInt): Boolean
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
|
@ -13,4 +13,6 @@ interface IErrorReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun noErrorForLine(position: Position): Boolean
|
fun noErrorForLine(position: Position): Boolean
|
||||||
|
|
||||||
|
fun printSingleError(errormessage: String)
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
package prog8.code.core
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
enum class CpuType {
|
|
||||||
CPU6502,
|
|
||||||
CPU65c02,
|
|
||||||
VIRTUAL
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IMachineDefinition {
|
|
||||||
val FLOAT_MAX_NEGATIVE: Double
|
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
|
||||||
val FLOAT_MEM_SIZE: Int
|
|
||||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
|
||||||
val PROGRAM_LOAD_ADDRESS : UInt
|
|
||||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
|
||||||
val BSSHIGHRAM_START: UInt
|
|
||||||
val BSSHIGHRAM_END: UInt
|
|
||||||
val BSSGOLDENRAM_START: UInt
|
|
||||||
val BSSGOLDENRAM_END: UInt
|
|
||||||
|
|
||||||
val cpu: CpuType
|
|
||||||
var zeropage: Zeropage
|
|
||||||
var golden: GoldenRam
|
|
||||||
|
|
||||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
|
||||||
fun getFloatAsmBytes(num: Number): String
|
|
||||||
|
|
||||||
fun convertFloatToBytes(num: Double): List<UByte>
|
|
||||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
|
||||||
|
|
||||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
|
||||||
fun isIOAddress(address: UInt): Boolean
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ interface IMemSizer {
|
|||||||
|
|
||||||
fun memorySize(dt: BaseDataType): Int {
|
fun memorySize(dt: BaseDataType): Int {
|
||||||
if(dt.isPassByRef)
|
if(dt.isPassByRef)
|
||||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
return memorySize(DataType.UWORD, null) // a pointer size
|
||||||
try {
|
try {
|
||||||
return memorySize(DataType.forDt(dt), null)
|
return memorySize(DataType.forDt(dt), null)
|
||||||
} catch (x: NoSuchElementException) {
|
} catch (x: NoSuchElementException) {
|
||||||
|
@ -9,7 +9,8 @@ enum class Encoding(val prefix: String) {
|
|||||||
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||||
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||||
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
||||||
KATAKANA("kata") // cx16 (katakana)
|
KATAKANA("kata"), // cx16 (katakana)
|
||||||
|
C64OS("c64os") // c64 (C64 OS)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IStringEncoding {
|
interface IStringEncoding {
|
||||||
|
@ -22,7 +22,7 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
|||||||
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||||
|
|
||||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||||
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
|
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
}
|
}
|
||||||
datatype.isFloat -> {
|
datatype.isFloat -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||||
if(position!=null)
|
if(position!=null)
|
||||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||||
else
|
else
|
||||||
@ -133,8 +133,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
require(size>0)
|
require(size>0)
|
||||||
return free.containsAll((address until address+size.toUInt()).toList())
|
return free.containsAll((address until address+size.toUInt()).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun allocateCx16VirtualRegisters()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +154,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||||
datatype.isFloat -> {
|
datatype.isFloat -> {
|
||||||
if (options.floats) {
|
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 return Err(MemAllocationError("floating point option not enabled"))
|
||||||
}
|
}
|
||||||
else -> throw MemAllocationError("weird dt")
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.sanitize
|
||||||
import prog8.code.source.SourceCode
|
import prog8.code.source.SourceCode
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
|||||||
if(SourceCode.isLibraryResource(file))
|
if(SourceCode.isLibraryResource(file))
|
||||||
return "$file:$line:$startCol:"
|
return "$file:$line:$startCol:"
|
||||||
return try {
|
return try {
|
||||||
val path = Path(file).absolute().normalize().toString()
|
val path = Path(file).sanitize().toString()
|
||||||
"file://$path:$line:$startCol:"
|
"file://$path:$line:$startCol:"
|
||||||
} catch(_: InvalidPathException) {
|
} catch(_: InvalidPathException) {
|
||||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.code.source
|
package prog8.code.source
|
||||||
|
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.sanitize
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.TreeMap
|
import java.util.*
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
|
|
||||||
// Resource caching "filesystem".
|
// Resource caching "filesystem".
|
||||||
@ -21,12 +21,12 @@ object ImportFileSystem {
|
|||||||
|
|
||||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||||
|
|
||||||
fun getFile(path: Path): SourceCode {
|
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||||
val normalized = path.absolute().normalize()
|
val normalized = path.sanitize()
|
||||||
val cached = cache[normalized.toString()]
|
val cached = cache[normalized.toString()]
|
||||||
if (cached != null)
|
if (cached != null)
|
||||||
return cached
|
return cached
|
||||||
val file = SourceCode.File(normalized)
|
val file = SourceCode.File(normalized, isLibrary)
|
||||||
cache[normalized.toString()] = file
|
cache[normalized.toString()] = file
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ object ImportFileSystem {
|
|||||||
val cached = cache[position.file]
|
val cached = cache[position.file]
|
||||||
if(cached != null)
|
if(cached != null)
|
||||||
return getLine(cached, position.line)
|
return getLine(cached, position.line)
|
||||||
val path = Path(position.file).absolute().normalize()
|
val path = Path(position.file).sanitize()
|
||||||
val cached2 = cache[path.toString()]
|
val cached2 = cache[path.toString()]
|
||||||
if(cached2 != null)
|
if(cached2 != null)
|
||||||
return getLine(cached2, position.line)
|
return getLine(cached2, position.line)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.code.source
|
package prog8.code.source
|
||||||
|
|
||||||
|
import prog8.code.sanitize
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.text.Normalizer
|
import java.text.Normalizer
|
||||||
@ -22,6 +23,11 @@ sealed class SourceCode {
|
|||||||
*/
|
*/
|
||||||
abstract val isFromFilesystem: Boolean
|
abstract val isFromFilesystem: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this [SourceCode] instance was created from a library module file
|
||||||
|
*/
|
||||||
|
abstract val isFromLibrary: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logical name of the source code unit. Usually the module's name.
|
* The logical name of the source code unit. Usually the module's name.
|
||||||
*/
|
*/
|
||||||
@ -54,7 +60,7 @@ sealed class SourceCode {
|
|||||||
private const val LIBRARYFILEPREFIX = "library:"
|
private const val LIBRARYFILEPREFIX = "library:"
|
||||||
private const val STRINGSOURCEPREFIX = "string:"
|
private const val STRINGSOURCEPREFIX = "string:"
|
||||||
val curdir: Path = Path(".").absolute()
|
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 isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||||
@ -76,6 +82,7 @@ sealed class SourceCode {
|
|||||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||||
override val isFromResources = false
|
override val isFromResources = false
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = false
|
||||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||||
override val name = "<unnamed-text>"
|
override val name = "<unnamed-text>"
|
||||||
}
|
}
|
||||||
@ -89,7 +96,7 @@ sealed class SourceCode {
|
|||||||
* @throws NoSuchFileException if the file does not exist
|
* @throws NoSuchFileException if the file does not exist
|
||||||
* @throws FileSystemException if the file cannot be read
|
* @throws FileSystemException if the file cannot be read
|
||||||
*/
|
*/
|
||||||
internal class File(path: Path): SourceCode() {
|
internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
|
||||||
override val text: String
|
override val text: String
|
||||||
override val origin: String
|
override val origin: String
|
||||||
override val name: String
|
override val name: String
|
||||||
@ -120,6 +127,7 @@ sealed class SourceCode {
|
|||||||
|
|
||||||
override val isFromResources = true
|
override val isFromResources = true
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = true
|
||||||
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||||
override val text: String
|
override val text: String
|
||||||
override val name: String
|
override val name: String
|
||||||
@ -146,6 +154,7 @@ sealed class SourceCode {
|
|||||||
class Generated(override val name: String) : SourceCode() {
|
class Generated(override val name: String) : SourceCode() {
|
||||||
override val isFromResources: Boolean = false
|
override val isFromResources: Boolean = false
|
||||||
override val isFromFilesystem: Boolean = false
|
override val isFromFilesystem: Boolean = false
|
||||||
|
override val isFromLibrary: Boolean = false
|
||||||
override val origin: String = name
|
override val origin: String = name
|
||||||
override val text: String = "<generated code node, no text representation>"
|
override val text: String = "<generated code node, no text representation>"
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
package prog8.code.target
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.atari.AtariMachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|
||||||
override val name = NAME
|
|
||||||
override val machine = AtariMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.ATASCII
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val NAME = "atari"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 * machine.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 -> machine.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,81 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.C128Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.c128.C128MachineDefinition
|
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
|
|
||||||
|
|
||||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class C128Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = C128MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c128"
|
const val NAME = "c128"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
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 lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C128Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,95 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.C64Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.io.IOException
|
||||||
import prog8.code.target.c64.C64MachineDefinition
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
|
|
||||||
|
|
||||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class C64Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = C64MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c64"
|
const val NAME = "c64"
|
||||||
|
|
||||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||||
|
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xc000u
|
||||||
|
override val BSSHIGHRAM_END = 0xcfdfu
|
||||||
|
override val BSSGOLDENRAM_START = 0u // no golden ram on C64
|
||||||
|
override val BSSGOLDENRAM_END = 0u
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
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")) {
|
||||||
|
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)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(_: IOException) {
|
||||||
|
continue // try the next emulator executable
|
||||||
|
}
|
||||||
|
process.waitFor()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C64Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -26,8 +98,6 @@ val CompilationTargets = listOf(
|
|||||||
C128Target.NAME,
|
C128Target.NAME,
|
||||||
Cx16Target.NAME,
|
Cx16Target.NAME,
|
||||||
PETTarget.NAME,
|
PETTarget.NAME,
|
||||||
AtariTarget.NAME,
|
|
||||||
Neo6502Target.NAME,
|
|
||||||
VMTarget.NAME
|
VMTarget.NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,8 +106,6 @@ fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
|
|||||||
C128Target.NAME -> C128Target()
|
C128Target.NAME -> C128Target()
|
||||||
Cx16Target.NAME -> Cx16Target()
|
Cx16Target.NAME -> Cx16Target()
|
||||||
PETTarget.NAME -> PETTarget()
|
PETTarget.NAME -> PETTarget()
|
||||||
AtariTarget.NAME -> AtariTarget()
|
|
||||||
VMTarget.NAME -> VMTarget()
|
VMTarget.NAME -> VMTarget()
|
||||||
Neo6502Target.NAME -> Neo6502Target()
|
|
||||||
else -> throw IllegalArgumentException("invalid compilation target")
|
else -> throw IllegalArgumentException("invalid compilation target")
|
||||||
}
|
}
|
||||||
|
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.source.ImportFileSystem.expandTilde
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.ConfigurableZeropage
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
|
import kotlin.io.path.isDirectory
|
||||||
|
import kotlin.io.path.nameWithoutExtension
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFileTarget(
|
||||||
|
override val name: String,
|
||||||
|
override val defaultEncoding: Encoding,
|
||||||
|
override val cpu: CpuType,
|
||||||
|
override val PROGRAM_LOAD_ADDRESS: UInt,
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS: UInt,
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE: UInt,
|
||||||
|
override val BSSHIGHRAM_START: UInt,
|
||||||
|
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?,
|
||||||
|
val ioAddresses: List<UIntRange>,
|
||||||
|
val zpScratchB1: UInt,
|
||||||
|
val zpScratchReg: UInt,
|
||||||
|
val zpScratchW1: UInt,
|
||||||
|
val zpScratchW2: UInt,
|
||||||
|
val virtualregistersStart: UInt,
|
||||||
|
val zpFullsafe: List<UIntRange>,
|
||||||
|
val zpKernalsafe: List<UIntRange>,
|
||||||
|
val zpBasicsafe: List<UIntRange>
|
||||||
|
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun Properties.getString(property: String): String {
|
||||||
|
val value = this.getProperty(property, null)
|
||||||
|
if(value!=null)
|
||||||
|
return value
|
||||||
|
throw NoSuchElementException("string property '$property' not found in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Properties.getInteger(property: String): UInt {
|
||||||
|
val value = this.getProperty(property, null)
|
||||||
|
if(value!=null) return parseInt(value)
|
||||||
|
throw NoSuchElementException("integer property '$property' not found in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseInt(value: String): UInt {
|
||||||
|
if(value.startsWith("0x"))
|
||||||
|
return value.drop(2).toUInt(16)
|
||||||
|
if(value.startsWith("$"))
|
||||||
|
return value.drop(1).toUInt(16)
|
||||||
|
if(value.startsWith("%"))
|
||||||
|
return value.drop(1).toUInt(2)
|
||||||
|
return value.toUInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
|
||||||
|
val rangesStr = props.getString(key)
|
||||||
|
if(rangesStr.isBlank())
|
||||||
|
return emptyList()
|
||||||
|
val result = mutableListOf<UIntRange>()
|
||||||
|
val ranges = rangesStr.split(",").map { it.trim() }
|
||||||
|
for(r in ranges) {
|
||||||
|
if ('-' in r) {
|
||||||
|
val (fromStr, toStr) = r.split("-")
|
||||||
|
val from = parseInt(fromStr.trim())
|
||||||
|
val to = parseInt(toStr.trim())
|
||||||
|
result.add(from..to)
|
||||||
|
} else {
|
||||||
|
val address = parseInt(r)
|
||||||
|
result.add(address..address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromConfigFile(configfile: Path): ConfigFileTarget {
|
||||||
|
val props = Properties()
|
||||||
|
props.load(configfile.inputStream())
|
||||||
|
|
||||||
|
val cpuString = props.getString("cpu").uppercase()
|
||||||
|
val cpuType = try {
|
||||||
|
CpuType.valueOf(cpuString)
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
CpuType.valueOf("CPU$cpuString")
|
||||||
|
}
|
||||||
|
val ioAddresses = parseAddressRanges("io_regions", props)
|
||||||
|
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
|
||||||
|
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
|
||||||
|
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
|
||||||
|
|
||||||
|
val libraryPath = expandTilde(Path(props.getString("library")))
|
||||||
|
if(!libraryPath.isDirectory())
|
||||||
|
throw IOException("invalid library path: $libraryPath")
|
||||||
|
|
||||||
|
val customLauncherStr = props.getProperty("custom_launcher_code", null)
|
||||||
|
val customLauncher =
|
||||||
|
if(customLauncherStr?.isNotBlank()==true)
|
||||||
|
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||||
|
else emptyList()
|
||||||
|
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||||
|
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||||
|
|
||||||
|
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||||
|
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||||
|
|
||||||
|
return ConfigFileTarget(
|
||||||
|
configfile.nameWithoutExtension,
|
||||||
|
Encoding.entries.first { it.prefix==props.getString("encoding") },
|
||||||
|
cpuType,
|
||||||
|
props.getInteger("load_address"),
|
||||||
|
props.getInteger("memtop"),
|
||||||
|
0u, // used only in a very specific error condition check in a certain scenario...
|
||||||
|
props.getInteger("bss_highram_start"),
|
||||||
|
props.getInteger("bss_highram_end"),
|
||||||
|
props.getInteger("bss_goldenram_start"),
|
||||||
|
props.getInteger("bss_goldenram_end"),
|
||||||
|
defaultOutputType,
|
||||||
|
libraryPath,
|
||||||
|
customLauncher,
|
||||||
|
assemblerOptions,
|
||||||
|
ioAddresses,
|
||||||
|
props.getInteger("zp_scratch_b1"),
|
||||||
|
props.getInteger("zp_scratch_reg"),
|
||||||
|
props.getInteger("zp_scratch_w1"),
|
||||||
|
props.getInteger("zp_scratch_w2"),
|
||||||
|
props.getInteger("virtual_registers"),
|
||||||
|
zpFullsafe,
|
||||||
|
zpKernalsafe,
|
||||||
|
zpBasicsafe,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO floats are not yet supported here, just enter some values
|
||||||
|
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||||
|
override val FLOAT_MEM_SIZE = 8
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam // TODO this is not yet used
|
||||||
|
|
||||||
|
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, quiet: Boolean) {
|
||||||
|
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = ConfigurableZeropage(
|
||||||
|
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||||
|
virtualregistersStart,
|
||||||
|
zpBasicsafe,
|
||||||
|
zpKernalsafe,
|
||||||
|
zpFullsafe,
|
||||||
|
compilerOptions
|
||||||
|
)
|
||||||
|
// note: there's no golden ram yet
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,94 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.CX16Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
import prog8.code.target.cx16.CX16MachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class Cx16Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = CX16MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "cx16"
|
const val NAME = "cx16"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU65C02
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||||
|
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||||
|
override val BSSGOLDENRAM_START = 0x0400u
|
||||||
|
override val BSSGOLDENRAM_END = 0x07ffu
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
val emulator: String
|
||||||
|
val extraArgs: List<String>
|
||||||
|
|
||||||
|
when(selectedEmulator) {
|
||||||
|
1 -> {
|
||||||
|
emulator = "x16emu"
|
||||||
|
extraArgs = listOf("-debug")
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
emulator = "box16"
|
||||||
|
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
|
||||||
|
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package prog8.code.target.cbm
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
@ -1,40 +0,0 @@
|
|||||||
package prog8.code.target
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.neo6502.Neo6502MachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|
||||||
override val name = NAME
|
|
||||||
override val machine = Neo6502MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.ISO
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val NAME = "neo"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 * machine.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 -> machine.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +1,34 @@
|
|||||||
package prog8.code.target.cbm
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.BaseDataType
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.core.IMemSizer
|
||||||
|
|
||||||
|
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||||
|
|
||||||
internal object CbmMemorySizer: IMemSizer {
|
|
||||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||||
if(dt.isArray) {
|
if(dt.isArray) {
|
||||||
if(numElements==null) return 2 // treat it as a pointer size
|
if(numElements==null) return 2 // treat it as a pointer size
|
||||||
return when(dt.sub) {
|
return when(dt.sub) {
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||||
BaseDataType.FLOAT-> numElements * Mflpt5.FLOAT_MEM_SIZE
|
BaseDataType.FLOAT-> numElements * floatsize
|
||||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
else -> throw IllegalArgumentException("invalid sub type")
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dt.isString) {
|
else if (dt.isString) {
|
||||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
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
|
?: 2 // treat it as the size to store a string pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
dt.isFloat -> Mflpt5.FLOAT_MEM_SIZE * (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.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
else -> 2 * (numElements ?: 1)
|
else -> 2 * (numElements ?: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,19 +1,80 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.PETZeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
import prog8.code.target.pet.PETMachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class PETTarget: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = PETMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "pet32"
|
const val NAME = "pet32"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u
|
||||||
|
override val BSSHIGHRAM_END = 0u
|
||||||
|
override val BSSGOLDENRAM_START = 0u
|
||||||
|
override val BSSGOLDENRAM_END = 0u
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process=processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = PETZeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,111 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.isReadable
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
class VMTarget: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(false),
|
||||||
|
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = VirtualMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.ISO
|
override val defaultEncoding = Encoding.ISO
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "virtual"
|
const val NAME = "virtual"
|
||||||
|
const val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
override val cpu = CpuType.VIRTUAL
|
||||||
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 * machine.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 {
|
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
else -> 2 * (numElements ?: 1)
|
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_END = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||||
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
|
override lateinit var golden: GoldenRam // not actually used
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number): String {
|
||||||
|
// 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" }
|
||||||
|
return parts.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val bits = num.toBits().toULong()
|
||||||
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==8) { "need 8 bytes" }
|
||||||
|
val b0 = bytes[0].toLong() shl (8*7)
|
||||||
|
val b1 = bytes[1].toLong() shl (8*6)
|
||||||
|
val b2 = bytes[2].toLong() shl (8*5)
|
||||||
|
val b3 = bytes[3].toLong() shl (8*4)
|
||||||
|
val b4 = bytes[4].toLong() shl (8*3)
|
||||||
|
val b5 = bytes[5].toLong() shl (8*2)
|
||||||
|
val b6 = bytes[6].toLong() shl (8*1)
|
||||||
|
val b7 = bytes[7].toLong() shl (8*0)
|
||||||
|
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, 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(), quiet)
|
||||||
|
} else {
|
||||||
|
val withExt = programNameWithPath.resolveSibling("$filename.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
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(irSource: String, quiet: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||||
|
override val SCRATCH_B1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_REG: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W1: UInt
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
package prog8.code.target.atari
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class AtariMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
|
||||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
|
||||||
override val FLOAT_MEM_SIZE = 6
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // TODO what's memtop?
|
|
||||||
|
|
||||||
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 lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
val emulatorName: String
|
|
||||||
val cmdline: List<String>
|
|
||||||
when(selectedEmulator) {
|
|
||||||
1 -> {
|
|
||||||
emulatorName = "atari800"
|
|
||||||
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
emulatorName = "altirra"
|
|
||||||
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
System.err.println("Atari target only supports atari800 and altirra emulators.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO monlist?
|
|
||||||
|
|
||||||
println("\nStarting Atari800XL emulator $emulatorName...")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = AtariZeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package prog8.code.target.atari
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.InternalCompilerException
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.code.core.ZeropageType
|
|
||||||
|
|
||||||
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
|
||||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
|
||||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
|
||||||
ZeropageType.FLOATSAFE,
|
|
||||||
ZeropageType.BASICSAFE,
|
|
||||||
ZeropageType.DONTUSE
|
|
||||||
))
|
|
||||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
|
||||||
|
|
||||||
when (options.zeropage) {
|
|
||||||
ZeropageType.FULL -> {
|
|
||||||
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
|
||||||
free.addAll(0x00u..0xffu)
|
|
||||||
// TODO atari free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
|
||||||
}
|
|
||||||
ZeropageType.KERNALSAFE -> {
|
|
||||||
free.addAll(0x80u..0xffu) // TODO
|
|
||||||
}
|
|
||||||
ZeropageType.BASICSAFE,
|
|
||||||
ZeropageType.FLOATSAFE -> {
|
|
||||||
free.addAll(0x80u..0xffu) // TODO
|
|
||||||
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
|
||||||
}
|
|
||||||
ZeropageType.DONTUSE -> {
|
|
||||||
free.clear() // don't use zeropage at all
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
|
||||||
free.clear()
|
|
||||||
free.addAll(distinctFree)
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
retainAllowed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if atari can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package prog8.code.target.c128
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class C128MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
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 lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\nStarting C-128 emulator x128...")
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
|
||||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = C128Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package prog8.code.target.c64
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class C64MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
|
||||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0xc000u
|
|
||||||
override val BSSHIGHRAM_END = 0xcfdfu
|
|
||||||
override val BSSGOLDENRAM_START = 0u
|
|
||||||
override val BSSGOLDENRAM_END = 0u
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process
|
|
||||||
try {
|
|
||||||
process=processb.start()
|
|
||||||
} catch(_: IOException) {
|
|
||||||
continue // try the next emulator executable
|
|
||||||
}
|
|
||||||
process.waitFor()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = C64Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package prog8.code.target.cx16
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class CX16MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU65c02
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
|
||||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
|
||||||
override val BSSGOLDENRAM_START = 0x0400u
|
|
||||||
override val BSSGOLDENRAM_END = 0x07ffu
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
val emulator: String
|
|
||||||
val extraArgs: List<String>
|
|
||||||
|
|
||||||
when(selectedEmulator) {
|
|
||||||
1 -> {
|
|
||||||
emulator = "x16emu"
|
|
||||||
extraArgs = listOf("-debug")
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
emulator = "box16"
|
|
||||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\nStarting Commander X16 emulator $emulator...")
|
|
||||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = CX16Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -26,7 +26,7 @@ object AtasciiEncoding {
|
|||||||
'▖',
|
'▖',
|
||||||
|
|
||||||
// $10
|
// $10
|
||||||
'♣',
|
'♣',
|
||||||
'┌',
|
'┌',
|
||||||
'─',
|
'─',
|
||||||
'┼',
|
'┼',
|
||||||
@ -62,7 +62,7 @@ object AtasciiEncoding {
|
|||||||
'/',
|
'/',
|
||||||
|
|
||||||
// $30
|
// $30
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
'2',
|
'2',
|
||||||
'3',
|
'3',
|
||||||
@ -80,7 +80,7 @@ object AtasciiEncoding {
|
|||||||
'?',
|
'?',
|
||||||
|
|
||||||
// $40
|
// $40
|
||||||
'@',
|
'@',
|
||||||
'A',
|
'A',
|
||||||
'B',
|
'B',
|
||||||
'C',
|
'C',
|
||||||
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
|||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\r' -> 0x9bu
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
|
||||||
|
object C64osEncoding {
|
||||||
|
|
||||||
|
// decoding: from C64 OS Screencodes (0-255) to unicode
|
||||||
|
// character table from:
|
||||||
|
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
|
||||||
|
|
||||||
|
private val decodingC64os = charArrayOf(
|
||||||
|
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||||
|
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
||||||
|
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
||||||
|
'c' , // c 0x03 -> LATIN SMALL LETTER C
|
||||||
|
'd' , // d 0x04 -> LATIN SMALL LETTER D
|
||||||
|
'e' , // e 0x05 -> LATIN SMALL LETTER E
|
||||||
|
'f' , // f 0x06 -> LATIN SMALL LETTER F
|
||||||
|
'g' , // g 0x07 -> LATIN SMALL LETTER G
|
||||||
|
'h' , // h 0x08 -> LATIN SMALL LETTER H
|
||||||
|
'i' , // i 0x09 -> LATIN SMALL LETTER I
|
||||||
|
'j' , // j 0x0A -> LATIN SMALL LETTER J
|
||||||
|
'k' , // k 0x0B -> LATIN SMALL LETTER K
|
||||||
|
'l' , // l 0x0C -> LATIN SMALL LETTER L
|
||||||
|
'm' , // m 0x0D -> LATIN SMALL LETTER M
|
||||||
|
'n' , // n 0x0E -> LATIN SMALL LETTER N
|
||||||
|
'o' , // o 0x0F -> LATIN SMALL LETTER O
|
||||||
|
'p' , // p 0x10 -> LATIN SMALL LETTER P
|
||||||
|
'q' , // q 0x11 -> LATIN SMALL LETTER Q
|
||||||
|
'r' , // r 0x12 -> LATIN SMALL LETTER R
|
||||||
|
's' , // s 0x13 -> LATIN SMALL LETTER S
|
||||||
|
't' , // t 0x14 -> LATIN SMALL LETTER T
|
||||||
|
'u' , // u 0x15 -> LATIN SMALL LETTER U
|
||||||
|
'v' , // v 0x16 -> LATIN SMALL LETTER V
|
||||||
|
'w' , // w 0x17 -> LATIN SMALL LETTER W
|
||||||
|
'x' , // x 0x18 -> LATIN SMALL LETTER X
|
||||||
|
'y' , // y 0x19 -> LATIN SMALL LETTER Y
|
||||||
|
'z' , // z 0x1A -> LATIN SMALL LETTER Z
|
||||||
|
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
|
||||||
|
'\\' , // \ 0x1C -> REVERSE SOLIDUS
|
||||||
|
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
|
||||||
|
'^' , // ^ 0x1E -> CIRCUMFLEX
|
||||||
|
'_' , // _ 0x1F -> UNDERSCORE
|
||||||
|
' ' , // 0x20 -> SPACE
|
||||||
|
'!' , // ! 0x21 -> EXCLAMATION MARK
|
||||||
|
'"' , // " 0x22 -> QUOTATION MARK
|
||||||
|
'#' , // # 0x23 -> NUMBER SIGN
|
||||||
|
'$' , // $ 0x24 -> DOLLAR SIGN
|
||||||
|
'%' , // % 0x25 -> PERCENT SIGN
|
||||||
|
'&' , // & 0x26 -> AMPERSAND
|
||||||
|
'\'' , // ' 0x27 -> APOSTROPHE
|
||||||
|
'(' , // ( 0x28 -> LEFT PARENTHESIS
|
||||||
|
')' , // ) 0x29 -> RIGHT PARENTHESIS
|
||||||
|
'*' , // * 0x2A -> ASTERISK
|
||||||
|
'+' , // + 0x2B -> PLUS SIGN
|
||||||
|
',' , // , 0x2C -> COMMA
|
||||||
|
'-' , // - 0x2D -> HYPHEN-MINUS
|
||||||
|
'.' , // . 0x2E -> FULL STOP
|
||||||
|
'/' , // / 0x2F -> SOLIDUS
|
||||||
|
'0' , // 0 0x30 -> DIGIT ZERO
|
||||||
|
'1' , // 1 0x31 -> DIGIT ONE
|
||||||
|
'2' , // 2 0x32 -> DIGIT TWO
|
||||||
|
'3' , // 3 0x33 -> DIGIT THREE
|
||||||
|
'4' , // 4 0x34 -> DIGIT FOUR
|
||||||
|
'5' , // 5 0x35 -> DIGIT FIVE
|
||||||
|
'6' , // 6 0x36 -> DIGIT SIX
|
||||||
|
'7' , // 7 0x37 -> DIGIT SEVEN
|
||||||
|
'8' , // 8 0x38 -> DIGIT EIGHT
|
||||||
|
'9' , // 9 0x39 -> DIGIT NINE
|
||||||
|
':' , // : 0x3A -> COLON
|
||||||
|
';' , // ; 0x3B -> SEMICOLON
|
||||||
|
'<' , // < 0x3C -> LESS-THAN SIGN
|
||||||
|
'=' , // = 0x3D -> EQUALS SIGN
|
||||||
|
'>' , // > 0x3E -> GREATER-THAN SIGN
|
||||||
|
'?' , // ? 0x3F -> QUESTION MARK
|
||||||
|
'`' , // ` 0x40 -> GRAVE ACCENT
|
||||||
|
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
|
||||||
|
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
|
||||||
|
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
|
||||||
|
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
|
||||||
|
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
|
||||||
|
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
|
||||||
|
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
|
||||||
|
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
|
||||||
|
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
|
||||||
|
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
|
||||||
|
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
|
||||||
|
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
|
||||||
|
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
|
||||||
|
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
|
||||||
|
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
|
||||||
|
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
|
||||||
|
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
|
||||||
|
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
|
||||||
|
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
|
||||||
|
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
|
||||||
|
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
|
||||||
|
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
|
||||||
|
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
|
||||||
|
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
|
||||||
|
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
|
||||||
|
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
|
||||||
|
'{' , // { 0x5B -> LEFT BRACE
|
||||||
|
'|' , // | 0x5C -> VERTICAL BAR
|
||||||
|
'}' , // } 0x5D -> RIGHT BRACE
|
||||||
|
'~' , // ~ 0x5E -> TILDE
|
||||||
|
'\ufffe', // 0x5F -> RESERVED
|
||||||
|
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
|
||||||
|
'\ufffe', // 0x61 -> COMMODORE SYMBOL
|
||||||
|
'\u2191', // ↑ 0x62 -> UP ARROW
|
||||||
|
'\u2193', // ↓ 0x63 -> DOWN ARROW
|
||||||
|
'\u2190', // ← 0x64 -> LEFT ARROW
|
||||||
|
'\u2192', // → 0x65 -> RIGHT ARROW
|
||||||
|
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
|
||||||
|
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
|
||||||
|
'\u2026', // … 0x68 -> ELLIPSIS
|
||||||
|
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
|
||||||
|
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
|
||||||
|
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
|
||||||
|
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
|
||||||
|
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
|
||||||
|
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
|
||||||
|
'\ufffe', // 0x6F -> UTILITY TITLE BAR
|
||||||
|
'\u00a9', // © 0x70 -> COPYRIGHT
|
||||||
|
'\u2713', // ✓ 0x71 -> CHECKMARK
|
||||||
|
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
|
||||||
|
'\ufffe', // 0x73 -> TICK TRACK
|
||||||
|
'\ufffe', // 0x74 -> TICK TRACK NUB
|
||||||
|
'\ufffe', // 0x75 -> TAB CORNER
|
||||||
|
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
|
||||||
|
'\ufffe', // 0x77 -> CUSTOM 1
|
||||||
|
'\ufffe', // 0x78 -> CUSTOM 2
|
||||||
|
'\ufffe', // 0x79 -> CUSTOM 3
|
||||||
|
'\ufffe', // 0x7A -> CUSTOM 4
|
||||||
|
'\ufffe', // 0x7B -> CUSTOM 5
|
||||||
|
'\ufffe', // 0x7C -> CUSTOM 6
|
||||||
|
'\ufffe', // 0x7D -> CUSTOM 7
|
||||||
|
'\ufffe', // 0x7E -> CUSTOM 8
|
||||||
|
'\ufffe', // 0x7F -> CUSTOM 9
|
||||||
|
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
|
||||||
|
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
|
||||||
|
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
|
||||||
|
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
|
||||||
|
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
|
||||||
|
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
|
||||||
|
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
|
||||||
|
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
|
||||||
|
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
|
||||||
|
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
|
||||||
|
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
|
||||||
|
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
|
||||||
|
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
|
||||||
|
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
|
||||||
|
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
|
||||||
|
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
|
||||||
|
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
|
||||||
|
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
|
||||||
|
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
|
||||||
|
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
|
||||||
|
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
|
||||||
|
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
|
||||||
|
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
|
||||||
|
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
|
||||||
|
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
|
||||||
|
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
|
||||||
|
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
|
||||||
|
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
|
||||||
|
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
|
||||||
|
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
|
||||||
|
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
|
||||||
|
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
|
||||||
|
'\ufffe', // 0xA0 -> REVERSED SPACE
|
||||||
|
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
|
||||||
|
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
|
||||||
|
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
|
||||||
|
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
|
||||||
|
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
|
||||||
|
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
|
||||||
|
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
|
||||||
|
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
|
||||||
|
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
|
||||||
|
'\ufffe', // 0xAA -> REVERSED ASTERISK
|
||||||
|
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
|
||||||
|
'\ufffe', // 0xAC -> REVERSED COMMA
|
||||||
|
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
|
||||||
|
'\ufffe', // 0xAE -> REVERSED FULL STOP
|
||||||
|
'\ufffe', // 0xAF -> REVERSED SOLIDUS
|
||||||
|
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
|
||||||
|
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
|
||||||
|
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
|
||||||
|
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
|
||||||
|
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
|
||||||
|
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
|
||||||
|
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
|
||||||
|
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
|
||||||
|
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
|
||||||
|
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
|
||||||
|
'\ufffe', // 0xBA -> REVERSED COLON
|
||||||
|
'\ufffe', // 0xBB -> REVERSED SEMICOLON
|
||||||
|
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
|
||||||
|
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
|
||||||
|
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
|
||||||
|
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
|
||||||
|
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
|
||||||
|
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
|
||||||
|
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
|
||||||
|
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
|
||||||
|
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
|
||||||
|
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
|
||||||
|
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
|
||||||
|
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
|
||||||
|
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
|
||||||
|
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
|
||||||
|
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
|
||||||
|
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
|
||||||
|
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
|
||||||
|
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
|
||||||
|
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
|
||||||
|
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
|
||||||
|
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
|
||||||
|
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
|
||||||
|
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
|
||||||
|
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
|
||||||
|
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
|
||||||
|
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
|
||||||
|
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
|
||||||
|
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
|
||||||
|
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
|
||||||
|
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
|
||||||
|
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
|
||||||
|
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
|
||||||
|
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
|
||||||
|
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
|
||||||
|
'\ufffe', // 0xDE -> REVERSED TILDE
|
||||||
|
'\ufffe', // 0xDF -> RESERVED
|
||||||
|
'\ufffe', // 0xE0 -> RESERVED
|
||||||
|
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
|
||||||
|
'\ufffe', // 0xE2 -> REVERSED UP ARROW
|
||||||
|
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
|
||||||
|
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
|
||||||
|
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
|
||||||
|
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
|
||||||
|
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
|
||||||
|
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
|
||||||
|
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
|
||||||
|
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
|
||||||
|
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
|
||||||
|
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
|
||||||
|
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
|
||||||
|
'\ufffe', // 0xEE -> MEMORY CHIP ICON
|
||||||
|
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
|
||||||
|
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
|
||||||
|
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
|
||||||
|
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
|
||||||
|
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
|
||||||
|
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
|
||||||
|
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
|
||||||
|
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
|
||||||
|
'\ufffe', // 0xF7 -> CUSTOM 10
|
||||||
|
'\ufffe', // 0xF8 -> CUSTOM 11
|
||||||
|
'\ufffe', // 0xF9 -> CUSTOM 12
|
||||||
|
'\ufffe', // 0xFA -> CUSTOM 13
|
||||||
|
'\ufffe', // 0xFB -> CUSTOM 14
|
||||||
|
'\ufffe', // 0xFC -> CUSTOM 15
|
||||||
|
'\ufffe', // 0xFD -> CUSTOM 16
|
||||||
|
'\ufffe', // 0xFE -> CUSTOM 17
|
||||||
|
'\ufffe' // 0xFF -> CUSTOM 18
|
||||||
|
)
|
||||||
|
|
||||||
|
// encoding: from unicode to C64 OS Screencodes (0-255)
|
||||||
|
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
|
||||||
|
|
||||||
|
private fun replaceSpecial(chr: Char): Char =
|
||||||
|
when(chr) {
|
||||||
|
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||||
|
else -> chr
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
|
val chr = replaceSpecial(chr3)
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if(chr.isISOControl())
|
||||||
|
throw CharConversionException("no c64os character for char #${chr.code}")
|
||||||
|
else
|
||||||
|
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
Ok(text.map {
|
||||||
|
try {
|
||||||
|
encodeChar(it, lowercase)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
encodeChar(it, !lowercase)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch(cx: CharConversionException) {
|
||||||
|
Err(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(screencode.map {
|
||||||
|
val code = it.toInt()
|
||||||
|
if(code<0 || code>= decodingC64os.size)
|
||||||
|
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
|
||||||
|
decodingC64os[code]
|
||||||
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,24 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.Encoding
|
||||||
import prog8.code.core.IStringEncoding
|
import prog8.code.core.IStringEncoding
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
import prog8.code.target.encodings.*
|
|
||||||
|
|
||||||
|
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||||
object Encoder: IStringEncoding {
|
|
||||||
override val defaultEncoding: Encoding = Encoding.ISO
|
override val defaultEncoding: Encoding = Encoding.ISO
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
val coded = when(encoding) {
|
val coded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||||
Encoding.ISO -> IsoEncoding.encode(str)
|
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
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")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
return coded.fold(
|
return coded.fold(
|
||||||
@ -31,12 +30,13 @@ object Encoder: IStringEncoding {
|
|||||||
val decoded = when(encoding) {
|
val decoded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
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")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
return decoded.fold(
|
return decoded.fold(
|
||||||
@ -44,4 +44,4 @@ object Encoder: IStringEncoding {
|
|||||||
success = { it }
|
success = { it }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
|||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
open class IsoEncodingBase(charsetName: String) {
|
open class IsoEncodingBase(charsetName: String) {
|
||||||
val charset: Charset = Charset.forName(charsetName)
|
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 {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(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 {
|
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) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
|||||||
object KatakanaEncoding {
|
object KatakanaEncoding {
|
||||||
val charset: Charset = Charset.forName("JIS_X0201")
|
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 {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
|
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
'\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 {
|
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) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ object PetsciiEncoding {
|
|||||||
'\ufffe', // 0x07 -> UNDEFINED
|
'\ufffe', // 0x07 -> UNDEFINED
|
||||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\ufffe', // 0x0A -> UNDEFINED
|
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||||
'\ufffe', // 0x0B -> UNDEFINED
|
'\ufffe', // 0x0B -> UNDEFINED
|
||||||
'\ufffe', // 0x0C -> UNDEFINED
|
'\ufffe', // 0x0C -> UNDEFINED
|
||||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||||
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
|||||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
return screencode?.toUByte() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> 141u
|
||||||
|
'\r' -> 141u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(chr.code - 0x8000).toUByte()
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
package prog8.code.target.neo6502
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class Neo6502MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU65c02
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
|
||||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
|
||||||
override val FLOAT_MEM_SIZE = 6
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0800u
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0xfc00u // kernal starts here
|
|
||||||
|
|
||||||
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 lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The neo target only supports the main emulator (neo).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val cmdline = listOf("neo", "${programNameWithPath}.bin@800", "cold")
|
|
||||||
|
|
||||||
println("\nStarting Neo6502 emulator...")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address in 0xff00u..0xff0fu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = Neo6502Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package prog8.code.target.neo6502
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
|
|
||||||
class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xfau // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG = 0xfbu // temp storage for a register, must be B1+1
|
|
||||||
override val SCRATCH_W1 = 0xfcu // temp storage 1 for a word $fc+$fd
|
|
||||||
override val SCRATCH_W2 = 0xfeu // temp storage 2 for a word $fe+$ff
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("Neo6502 target doesn't support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
when (options.zeropage) {
|
|
||||||
ZeropageType.DONTUSE -> {
|
|
||||||
free.clear() // don't use zeropage at all
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
free.addAll(0x22u..0xffu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
|
||||||
free.clear()
|
|
||||||
free.addAll(distinctFree)
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
allocateCx16VirtualRegisters()
|
|
||||||
retainAllowed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
|
||||||
// However, to be able for the compiler to "see" them as 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package prog8.code.target.pet
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class PETMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0u
|
|
||||||
override val BSSHIGHRAM_END = 0u
|
|
||||||
override val BSSGOLDENRAM_START = 0u
|
|
||||||
override val BSSGOLDENRAM_END = 0u
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 process=processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = PETZeropage(compilerOptions)
|
|
||||||
// there's no golden ram.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package prog8.code.target.virtual
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.isReadable
|
|
||||||
import kotlin.io.path.name
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class VirtualMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
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_MEM_SIZE = 8 // 64-bits double
|
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
|
||||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0u // not actually used
|
|
||||||
override val BSSHIGHRAM_END = 0u // not actually used
|
|
||||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
|
||||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
|
||||||
override lateinit var zeropage: Zeropage // not actually used
|
|
||||||
override lateinit var golden: GoldenRam // not actually used
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number): String {
|
|
||||||
// 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 }
|
|
||||||
return parts.joinToString(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val bits = num.toBits().toULong()
|
|
||||||
val hexStr = bits.toString(16).padStart(16, '0')
|
|
||||||
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==8) { "need 8 bytes" }
|
|
||||||
val b0 = bytes[0].toLong() shl (8*7)
|
|
||||||
val b1 = bytes[1].toLong() shl (8*6)
|
|
||||||
val b2 = bytes[2].toLong() shl (8*5)
|
|
||||||
val b3 = bytes[3].toLong() shl (8*4)
|
|
||||||
val b4 = bytes[4].toLong() shl (8*3)
|
|
||||||
val b5 = bytes[5].toLong() shl (8*2)
|
|
||||||
val b6 = bytes[6].toLong() shl (8*1)
|
|
||||||
val b7 = bytes[7].toLong() shl (8*0)
|
|
||||||
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...")
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = false
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = VirtualZeropage(compilerOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IVirtualMachineRunner {
|
|
||||||
fun runProgram(irSource: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
|
||||||
override val SCRATCH_B1: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_REG: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_W1: UInt
|
|
||||||
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 fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.c128
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
@ -11,17 +11,11 @@ import prog8.code.core.ZeropageType
|
|||||||
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
|
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_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_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
init {
|
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(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -71,8 +65,4 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if C128 can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.c64
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
|
override val SCRATCH_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_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_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
@ -79,18 +79,18 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
private fun allocateCx16VirtualRegisters() {
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
// 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)
|
// 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).
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
for(reg in 0..15) {
|
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}"] = VarAllocation((4+reg*2).toUInt(), DataType.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}s"] = VarAllocation((4+reg*2).toUInt(), DataType.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}L"] = VarAllocation((4+reg*2).toUInt(), DataType.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}H"] = VarAllocation((5+reg*2).toUInt(), DataType.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}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.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}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
free.remove((4+reg*2).toUInt())
|
free.remove((4+reg*2).toUInt())
|
||||||
free.remove((5+reg*2).toUInt())
|
free.remove((5+reg*2).toUInt())
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.cx16
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
|
override val SCRATCH_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_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_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
@ -52,17 +52,17 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
private fun allocateCx16VirtualRegisters() {
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
// 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)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
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.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
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.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
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.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
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.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
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.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
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
|
||||||
|
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||||
|
basicsafe: List<UIntRange>,
|
||||||
|
kernalsafe: List<UIntRange>,
|
||||||
|
fullsafe: List<UIntRange>,
|
||||||
|
options: CompilationOptions
|
||||||
|
) : Zeropage(options) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
TODO("floats in configurable target zp")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||||
|
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
|
||||||
|
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 in configurable target zp")
|
||||||
|
}
|
||||||
|
|
||||||
|
val distinctFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
allocateCx16VirtualRegisters()
|
||||||
|
retainAllowed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allocateCx16VirtualRegisters() {
|
||||||
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
|
// However, to be able for the compiler to "see" them as 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) {
|
||||||
|
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||||
|
if(address<=0xffu) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.pet
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
@ -11,15 +11,11 @@ import prog8.code.core.ZeropageType
|
|||||||
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
|
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_W1 = 0xb6u // temp storage 1 for a word
|
||||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -52,8 +48,4 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if PET can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -6,9 +6,10 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":codeCore"))
|
implementation(project(":codeCore"))
|
||||||
|
implementation(project(":simpleAst"))
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
|
|
||||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||||
@ -43,4 +44,4 @@ tasks.test {
|
|||||||
testLogging {
|
testLogging {
|
||||||
events("skipped", "failed")
|
events("skipped", "failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<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="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.assertions.core.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.runner.junit5.jvm" level="project" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,16 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.GENERATED_LABEL_PREFIX
|
||||||
import prog8.code.StConstant
|
import prog8.code.StConstant
|
||||||
import prog8.code.StMemVar
|
import prog8.code.StMemVar
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtLabel
|
import prog8.code.core.ICompilationTarget
|
||||||
import prog8.code.core.IMachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
||||||
|
|
||||||
var numberOfOptimizations = 0
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
|||||||
|
|
||||||
private fun optimizeSameAssignments(
|
private fun optimizeSameAssignments(
|
||||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||||
machine: IMachineDefinition,
|
machine: ICompilationTarget,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
|
|
||||||
@ -362,7 +362,7 @@ or *_afterif labels.
|
|||||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
|
val autoLabelPrefix = GENERATED_LABEL_PREFIX
|
||||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||||
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||||
@ -386,7 +386,7 @@ This gets generated after certain if conditions, and only the branch instruction
|
|||||||
|
|
||||||
private fun optimizeStoreLoadSame(
|
private fun optimizeStoreLoadSame(
|
||||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||||
machine: IMachineDefinition,
|
machine: ICompilationTarget,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -459,7 +459,7 @@ private fun optimizeStoreLoadSame(
|
|||||||
return mods
|
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? {
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
// try to get the constant value address, could return null if it's a symbol instead
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
@ -508,9 +508,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
|||||||
|
|
||||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// jsr Sub + rts -> jmp Sub
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
// jmp Sub + rts -> jmp Sub
|
||||||
// rts + jmp -> remove jmp
|
// rts + jmp -> remove jmp
|
||||||
// rts + bxx -> remove bxx
|
// rts + bxx -> remove bxx
|
||||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
// 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.
|
// and some other optimizations.
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -520,7 +522,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
|||||||
val third = lines[2].value
|
val third = lines[2].value
|
||||||
|
|
||||||
if(!haslabel(second)) {
|
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!!
|
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[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
mods += Modification(lines[1].index, true, null)
|
mods += Modification(lines[1].index, true, null)
|
||||||
@ -563,6 +568,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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -687,18 +701,20 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
|||||||
// phy + ldy + pla -> tya + ldy
|
// phy + ldy + pla -> tya + ldy
|
||||||
// phx + ldx + pla -> txa + ldx
|
// phx + ldx + pla -> txa + ldx
|
||||||
// pha + lda + pla -> nop
|
// pha + lda + pla -> nop
|
||||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
when (first) {
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " tya"))
|
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))
|
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " txa"))
|
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))
|
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.ast.PtLabel
|
import prog8.code.GENERATED_LABEL_PREFIX
|
||||||
import prog8.code.core.*
|
import prog8.code.IAssemblyProgram
|
||||||
import prog8.code.target.AtariTarget
|
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.C64Target
|
||||||
import prog8.code.target.Neo6502Target
|
import prog8.code.target.PETTarget
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
@ -19,21 +23,17 @@ internal class AssemblyProgram(
|
|||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||||
private val listFile = outputDir.resolve("$name.list")
|
private val listFile = outputDir.resolve("$name.list")
|
||||||
private val targetWithoutBreakpointsForEmulator = arrayOf(AtariTarget.NAME, Neo6502Target.NAME)
|
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
|
|
||||||
val assemblerCommand: List<String>
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
when (compTarget.name) {
|
when(options.output) {
|
||||||
in arrayOf("c64", "c128", "cx16", "pet32") -> {
|
OutputType.PRG -> {
|
||||||
// CBM machines .prg generation.
|
// CBM machines .prg generation.
|
||||||
|
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
|
||||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
if(options.warnSymbolShadowing)
|
||||||
command.add("-Wshadow")
|
command.add("-Wshadow")
|
||||||
@ -44,34 +44,62 @@ internal class AssemblyProgram(
|
|||||||
command.add("--quiet")
|
command.add("--quiet")
|
||||||
|
|
||||||
if(options.asmListfile) {
|
if(options.asmListfile) {
|
||||||
command.addAll(listOf("--list=$listFile", "--no-monitor"))
|
command.add("--list=$listFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||||
OutputType.PRG -> {
|
|
||||||
command.add("--cbm-prg")
|
|
||||||
println("\nCreating prg for target ${compTarget.name}.")
|
|
||||||
prgFile
|
|
||||||
}
|
|
||||||
OutputType.RAW -> {
|
|
||||||
command.add("--nostart")
|
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
|
||||||
binFile
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid output type")
|
|
||||||
}
|
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
}
|
}
|
||||||
"atari" -> {
|
OutputType.XEX -> {
|
||||||
// Atari800XL .xex generation.
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
// TODO are these options okay for atari?
|
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
|
|
||||||
|
if(options.warnSymbolShadowing)
|
||||||
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
|
}
|
||||||
|
OutputType.RAW -> {
|
||||||
|
// Neo6502/headerless raw program generation.
|
||||||
|
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
|
|
||||||
|
if(options.warnSymbolShadowing)
|
||||||
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
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)
|
||||||
|
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
"--no-monitor"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
if(options.warnSymbolShadowing)
|
||||||
command.add("-Wshadow")
|
command.add("-Wshadow")
|
||||||
@ -84,63 +112,30 @@ internal class AssemblyProgram(
|
|||||||
if(options.asmListfile)
|
if(options.asmListfile)
|
||||||
command.add("--list=$listFile")
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||||
OutputType.XEX -> {
|
if(!options.quiet)
|
||||||
command.add("--atari-xex")
|
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||||
println("\nCreating xex for target ${compTarget.name}.")
|
command.add("--cbm-prg")
|
||||||
xexFile
|
} else {
|
||||||
}
|
if(!options.quiet)
|
||||||
OutputType.RAW -> {
|
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||||
command.add("--nostart")
|
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
|
||||||
binFile
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid output type")
|
|
||||||
}
|
}
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
}
|
}
|
||||||
"neo" -> {
|
|
||||||
// Neo6502 raw program generation.
|
|
||||||
|
|
||||||
if(options.output!=OutputType.RAW || options.loadAddress!=0x0800u || options.launcher!=CbmPrgLauncherType.NONE) {
|
|
||||||
throw AssemblyError("invalid program compilation options. Neo6502 requires %output raw, %launcher none, %address $0800")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO are these options okay for neo?
|
|
||||||
val command = mutableListOf("64tass", "--case-sensitive", "--long-branch",
|
|
||||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
|
||||||
"--no-monitor"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
|
||||||
command.add("-Wshadow")
|
|
||||||
else
|
|
||||||
command.add("-Wno-shadow")
|
|
||||||
|
|
||||||
if(options.asmQuiet)
|
|
||||||
command.add("--quiet")
|
|
||||||
|
|
||||||
if(options.asmListfile)
|
|
||||||
command.add("--list=$listFile")
|
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
|
||||||
OutputType.RAW -> {
|
|
||||||
command.add("--nostart")
|
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
|
||||||
binFile
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid output type, need 'raw'")
|
|
||||||
}
|
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid compilation target")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||||
val result = proc.waitFor()
|
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||||
if (result == 0 && compTarget.name !in targetWithoutBreakpointsForEmulator) {
|
|
||||||
|
val proc = ProcessBuilder(assemblerCommand)
|
||||||
|
if(!options.quiet)
|
||||||
|
proc.inheritIO()
|
||||||
|
val process = proc.start()
|
||||||
|
val result = process.waitFor()
|
||||||
|
if (result == 0) {
|
||||||
removeGeneratedLabelsFromMonlist()
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
@ -148,7 +143,7 @@ internal class AssemblyProgram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
|
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
|
||||||
val lines = viceMonListFile.toFile().readLines()
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
@ -165,7 +160,7 @@ internal class AssemblyProgram(
|
|||||||
for (line in viceMonListFile.toFile().readLines()) {
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if (match != null)
|
if (match != null)
|
||||||
breakpoints.add("break \$" + match.groupValues[1])
|
breakpoints.add("break $" + match.groupValues[1])
|
||||||
}
|
}
|
||||||
val num = breakpoints.size
|
val num = breakpoints.size
|
||||||
breakpoints.add(0, "; breakpoint list now follows")
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
|
@ -53,9 +53,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val memread = PtMemoryByte(fcall.position)
|
val memread = PtMemoryByte(fcall.position)
|
||||||
memread.add(fcall.args[0])
|
memread.add(fcall.args[0])
|
||||||
memread.parent = fcall
|
memread.parent = fcall
|
||||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
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.assignExpressionTo(fcall.args[1], memtarget)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
}
|
}
|
||||||
@ -66,8 +66,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||||
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
||||||
"call" -> funcCall(fcall)
|
"call" -> funcCall(fcall)
|
||||||
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
|
|
||||||
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
|
|
||||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||||
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
||||||
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
||||||
@ -109,8 +107,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
val var3name = asmgen.asmVariableName(fcall.args[3] 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 divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.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 remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
||||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
||||||
}
|
}
|
||||||
@ -122,7 +120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
||||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
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)
|
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -141,7 +139,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRsave() {
|
private fun funcRsave() {
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
php
|
php
|
||||||
pha
|
pha
|
||||||
@ -161,7 +159,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRrestore() {
|
private fun funcRrestore() {
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
plx
|
plx
|
||||||
ply
|
ply
|
||||||
@ -198,7 +196,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
return
|
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("""
|
asmgen.out("""
|
||||||
; push a return address so the jmp becomes indirect jsr
|
; push a return address so the jmp becomes indirect jsr
|
||||||
lda #>((+)-1)
|
lda #>((+)-1)
|
||||||
@ -229,6 +227,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
.word ${constAddress.toHex()}
|
.word ${constAddress.toHex()}
|
||||||
.byte $constBank""")
|
.byte $constBank""")
|
||||||
} else {
|
} 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.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||||
asmgen.out(" sta (++)+0")
|
asmgen.out(" sta (++)+0")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||||
@ -285,6 +286,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
.word ${constAddress.toHex()}
|
.word ${constAddress.toHex()}
|
||||||
.byte $constBank""")
|
.byte $constBank""")
|
||||||
} else {
|
} 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.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||||
asmgen.out(" sta (++)+0")
|
asmgen.out(" sta (++)+0")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||||
@ -348,7 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
cmp ${asmgen.asmVariableName(arg2)}
|
cmp ${asmgen.asmVariableName(arg2)}
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
is PtBool -> TODO("word compare against bool")
|
is PtBool -> TODO("word compare against bool ${arg2.position}")
|
||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -376,13 +380,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||||
val name = (fcall.args[0] as PtString).value
|
val name = (fcall.args[0] as PtString).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
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 slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||||
val addressOf = PtAddressOf(fcall.position)
|
val addressOf = PtAddressOf(fcall.position)
|
||||||
addressOf.add(slabname)
|
addressOf.add(slabname)
|
||||||
addressOf.parent = fcall
|
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 target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,12 +417,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
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")
|
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.address as PtNumber).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #$80 |+ | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||||
@ -426,7 +430,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -437,13 +441,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||||
val varname = asmgen.asmVariableName(what.variable)
|
val varname = asmgen.asmVariableName(what.variable)
|
||||||
if(what.splitWords)
|
if(what.splitWords)
|
||||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
|
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||||
else
|
else
|
||||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
|
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #$80 | sta ${varname}+1,x |+ ")
|
||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -465,29 +469,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" ror ${varname},x")
|
asmgen.out(" ror ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
when {
|
||||||
val number = (what.address as PtNumber).number
|
what.address is PtNumber -> {
|
||||||
asmgen.out(" ror ${number.toHex()}")
|
val number = (what.address as PtNumber).number
|
||||||
} else {
|
asmgen.out(" ror ${number.toHex()}")
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
}
|
||||||
if(ptrAndIndex!=null) {
|
what.address is PtIdentifier -> {
|
||||||
asmgen.out(" php")
|
asmgen.out(" php") // save Carry
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
asmgen.out(" plp | ror a")
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
}
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
else -> {
|
||||||
asmgen.out("""
|
TODO("ror ptr-expression ${what.position}")
|
||||||
plp
|
|
||||||
+ ror ${'$'}ffff,x ; modified""")
|
|
||||||
} 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""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -529,12 +523,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
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")
|
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.address as PtNumber).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | cmp #$80 | rol a | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||||
@ -542,7 +536,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -553,7 +547,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||||
val varname = asmgen.asmVariableName(what.variable)
|
val varname = asmgen.asmVariableName(what.variable)
|
||||||
if(what.splitWords)
|
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
|
else
|
||||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||||
}
|
}
|
||||||
@ -581,29 +575,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" rol ${varname},x")
|
asmgen.out(" rol ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
when {
|
||||||
val number = (what.address as PtNumber).number
|
what.address is PtNumber -> {
|
||||||
asmgen.out(" rol ${number.toHex()}")
|
val number = (what.address as PtNumber).number
|
||||||
} else {
|
asmgen.out(" rol ${number.toHex()}")
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
}
|
||||||
if(ptrAndIndex!=null) {
|
what.address is PtIdentifier -> {
|
||||||
asmgen.out(" php")
|
asmgen.out(" php") // save Carry
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
asmgen.out(" plp | rol a")
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
}
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
else -> {
|
||||||
asmgen.out("""
|
TODO("rol ptr-expression ${what.position}")
|
||||||
plp
|
|
||||||
+ rol ${'$'}ffff,x ; modified""")
|
|
||||||
} 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""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -642,7 +626,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
when(fcall.args[0]) {
|
when(fcall.args[0]) {
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
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 -> {
|
is PtArrayIndexer -> {
|
||||||
val indexer = fcall.args[0] as PtArrayIndexer
|
val indexer = fcall.args[0] as PtArrayIndexer
|
||||||
@ -684,7 +668,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
multipliedIndex.parent=indexer
|
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]}")
|
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||||
}
|
}
|
||||||
@ -692,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
if(fcall.args[1].asConstInteger() == 0) {
|
if(fcall.args[1].asConstInteger() == 0) {
|
||||||
assignAsmGen.assignConstantByte(target, 0)
|
assignAsmGen.assignConstantByte(target, 0)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,8 +739,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.assignConstFloatToPointerAY(number)
|
asmgen.assignConstFloatToPointerAY(number)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
|
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
pha
|
pha
|
||||||
@ -783,7 +767,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
if(asmgen.isZpVar(addrExpr)) {
|
if(asmgen.isZpVar(addrExpr)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta ($varname)
|
sta ($varname)
|
||||||
txa
|
txa
|
||||||
@ -831,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" jsr floats.MOVFM")
|
asmgen.out(" jsr floats.MOVFM")
|
||||||
if(resultRegister!=null) {
|
if(resultRegister!=null) {
|
||||||
assignAsmGen.assignFAC1float(
|
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(), fcall.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,7 +833,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val varname = asmgen.asmVariableName(addrExpr)
|
val varname = asmgen.asmVariableName(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr)) {
|
if(asmgen.isZpVar(addrExpr)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #1
|
ldy #1
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
@ -1035,38 +1019,58 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||||
val reg = resultRegister ?: RegisterOrPair.AY
|
val reg = resultRegister ?: RegisterOrPair.AY
|
||||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
var needAsaveForArg0 = asmgen.needAsaveForExpr(fcall.args[0])
|
||||||
if(!needAsave) {
|
if(!needAsaveForArg0) {
|
||||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||||
if (mr0 != null)
|
if (mr0 != null)
|
||||||
needAsave = mr0.address !is PtNumber
|
needAsaveForArg0 = mr0.address !is PtNumber
|
||||||
if (mr1 != null)
|
if (mr1 != null)
|
||||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
needAsaveForArg0 = needAsaveForArg0 or (mr1.address !is PtNumber)
|
||||||
}
|
}
|
||||||
when(reg) {
|
when(reg) {
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||||
if(needAsave)
|
// first 0 then 1
|
||||||
asmgen.out(" pha")
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
if(needAsave)
|
} else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||||
asmgen.out(" pla")
|
// first 1 then 0
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
|
if (needAsaveForArg0)
|
||||||
|
asmgen.out(" pha")
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||||
|
if (needAsaveForArg0)
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RegisterOrPair.AY -> {
|
RegisterOrPair.AY -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||||
if(needAsave)
|
// first 0 then 1
|
||||||
asmgen.out(" pha")
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
if(needAsave)
|
} else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||||
asmgen.out(" pla")
|
// first 1 then 0
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
|
if (needAsaveForArg0)
|
||||||
|
asmgen.out(" pha")
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||||
|
if (needAsaveForArg0)
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
if(needAsave)
|
if (needAsaveForArg0)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||||
if(needAsave)
|
if (needAsaveForArg0)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
asmgen.out(" tax")
|
asmgen.out(" tax")
|
||||||
}
|
}
|
||||||
@ -1097,7 +1101,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
val regname = resultRegister.name.lowercase()
|
val regname = resultRegister.name.lowercase()
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||||
@ -1197,7 +1201,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
val regname = resultRegister.name.lowercase()
|
val regname = resultRegister.name.lowercase()
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||||
@ -1285,11 +1289,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||||
|
|
||||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
|
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||||
val addr = PtAddressOf(value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
addr.add(variable)
|
addr.add(variable)
|
||||||
addr.parent = call
|
addr.parent = call
|
||||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
|
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1316,7 +1320,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign, scope)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
conv.reg != null -> {
|
conv.reg != null -> {
|
||||||
@ -1334,7 +1338,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign, scope)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callconv")
|
else -> throw AssemblyError("callconv")
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package prog8.codegen.cpu6502
|
|
||||||
|
|
||||||
import prog8.code.ast.IPtSubroutine
|
|
||||||
import prog8.code.ast.PtAsmSub
|
|
||||||
import prog8.code.ast.PtSub
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.RegisterOrPair
|
|
||||||
import prog8.code.core.RegisterOrStatusflag
|
|
||||||
|
|
||||||
|
|
||||||
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
|
||||||
when(this) {
|
|
||||||
is PtAsmSub -> {
|
|
||||||
return returns
|
|
||||||
}
|
|
||||||
is PtSub -> {
|
|
||||||
// for non-asm subroutines, determine the return registers based on the type of the return value
|
|
||||||
return if(returntype==null)
|
|
||||||
emptyList()
|
|
||||||
else {
|
|
||||||
val register = when {
|
|
||||||
returntype!!.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
|
||||||
returntype!!.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
returntype!!.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
|
||||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
}
|
|
||||||
listOf(Pair(register, returntype!!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
|
||||||
return when {
|
|
||||||
returntype?.isByteOrBool==true -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
|
||||||
returntype?.isWord==true -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
returntype?.isFloat==true -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
|
||||||
returntype==null -> null
|
|
||||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,224 +33,351 @@ internal class ForLoopsAsmGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
if(range.step.asConstInteger()!! < -1) {
|
||||||
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) {
|
|
||||||
val limit = range.to.asConstInteger()
|
val limit = range.to.asConstInteger()
|
||||||
if(limit==0)
|
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")
|
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 {
|
when {
|
||||||
iterableDt.isByteArray -> {
|
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
}
|
||||||
// simple loop downto 0 step -1
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.loopEndLabels.removeLast()
|
||||||
asmgen.translate(stmt.statements)
|
}
|
||||||
|
|
||||||
|
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("""
|
asmgen.out("""
|
||||||
dec $varname
|
clc
|
||||||
lda $varname
|
sbc $varname
|
||||||
cmp #255
|
bvc +
|
||||||
bne $loopLabel""")
|
eor #$80
|
||||||
|
+ bpl $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else {
|
||||||
// simple loop downto 1 step -1
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
sec
|
||||||
bne $loopLabel""")
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==1 || stepsize==-1) {
|
} else {
|
||||||
// bytes array, step 1 or -1
|
if(stepsize<0) {
|
||||||
|
|
||||||
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)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
cmp $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
beq +
|
||||||
beq $endLabel
|
bcs $endLabel
|
||||||
$incdec $varname""")
|
|
||||||
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
|
|
||||||
+""")
|
+""")
|
||||||
else
|
}
|
||||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
else {
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
}
|
|
||||||
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""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
sec
|
|
||||||
sbc #${stepsize.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bpl $loopLabel""")
|
|
||||||
}
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
asmgen.out(loopLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
asmgen.translate(forloop.statements)
|
||||||
assignLoopvarWord(stmt, range)
|
asmgen.out("""
|
||||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
lda $varname
|
||||||
// simple loop downto 0 step -1 (words)
|
cmp $toValueVar
|
||||||
asmgen.out(loopLabel)
|
beq $endLabel
|
||||||
asmgen.translate(stmt.statements)
|
$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("""
|
asmgen.out("""
|
||||||
lda $varname
|
clc
|
||||||
bne ++
|
sbc $varname
|
||||||
lda $varname+1
|
bvc +
|
||||||
beq $endLabel
|
eor #$80
|
||||||
+ lda $varname
|
+ bpl $endLabel""")
|
||||||
bne +
|
|
||||||
dec $varname+1
|
|
||||||
+ dec $varname""")
|
|
||||||
asmgen.jmp(loopLabel)
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else
|
||||||
// simple loop downto 1 step -1 (words)
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
sec
|
||||||
cmp #1
|
sbc $varname
|
||||||
bne +
|
bvc +
|
||||||
lda $varname+1
|
eor #$80
|
||||||
beq $endLabel
|
+ bmi $endLabel""")
|
||||||
+ lda $varname
|
} else {
|
||||||
bne +
|
if(stepsize<0) {
|
||||||
dec $varname+1
|
asmgen.out("""
|
||||||
+ dec $varname""")
|
cmp $varname
|
||||||
asmgen.jmp(loopLabel)
|
beq +
|
||||||
asmgen.out(endLabel)
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
else if (stepsize == 1 || stepsize == -1) {
|
else {
|
||||||
// words, step 1 or -1
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
}
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
asmgen.out("""
|
}
|
||||||
|
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
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(forloop.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel2 cmp #0 ; modified
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
if(stepsize==1) {
|
if(stepsize==1) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1""")
|
inc $varname+1""")
|
||||||
asmgen.jmp(loopLabel)
|
asmgen.jmp(loopLabel)
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname""")
|
+ dec $varname""")
|
||||||
asmgen.jmp(loopLabel)
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
else if (stepsize > 0) {
|
}
|
||||||
// (u)words, step >= 2
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
// (u)words, step >= 2
|
||||||
asmgen.out("""
|
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
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if (iterableDt.isUnsignedWordArray) {
|
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||||
asmgen.out("""
|
if (iterableDt.isUnsignedWordArray) {
|
||||||
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -266,8 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
|||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bcs $loopLabel
|
bcs $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -283,20 +410,23 @@ $modifiedLabel lda #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
|
||||||
// (u)words, step <= -2
|
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
// (u)words, step <= -2
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
val stepsize = range.step.asConstInteger()!!
|
||||||
asmgen.out("""
|
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
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
lda $varname
|
||||||
sec
|
sec
|
||||||
sbc #<${stepsize.absoluteValue}
|
sbc #<${stepsize.absoluteValue}
|
||||||
@ -313,12 +443,6 @@ $modifiedLabel sbc #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||||
@ -330,23 +454,23 @@ $endLabel""")
|
|||||||
sta P8ZP_SCRATCH_W2 ; to
|
sta P8ZP_SCRATCH_W2 ; to
|
||||||
sty P8ZP_SCRATCH_W2+1 ; to
|
sty P8ZP_SCRATCH_W2+1 ; to
|
||||||
lda $fromVar
|
lda $fromVar
|
||||||
cmp P8ZP_SCRATCH_W2
|
cmp P8ZP_SCRATCH_W2
|
||||||
lda $fromVar+1
|
lda $fromVar+1
|
||||||
sbc P8ZP_SCRATCH_W2+1
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
ldy P8ZP_SCRATCH_W2+1""")
|
ldy P8ZP_SCRATCH_W2+1""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
tya
|
tya
|
||||||
sbc $fromVar+1
|
sbc $fromVar+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_REG""")
|
lda P8ZP_SCRATCH_REG""")
|
||||||
} else {
|
} else {
|
||||||
if(stepsize<0)
|
if(stepsize<0)
|
||||||
@ -362,11 +486,11 @@ $endLabel""")
|
|||||||
+""")
|
+""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy $fromVar+1
|
cpy $fromVar+1
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bne +
|
bne +
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,24 +507,42 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
iterableDt.isString -> {
|
iterableDt.isString -> {
|
||||||
asmgen.out("""
|
if(asmgen.options.romable) {
|
||||||
lda #<$iterableName
|
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
ldy #>$iterableName
|
asmgen.out("""
|
||||||
sta $loopLabel+1
|
ldy #0
|
||||||
sty $loopLabel+2
|
sty $indexVar
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda $iterableName,y
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $loopLabel+1
|
inc $indexVar
|
||||||
bne $loopLabel
|
ldy $indexVar
|
||||||
inc $loopLabel+2
|
bne $loopLabel
|
||||||
bne $loopLabel
|
|
||||||
$endLabel""")
|
$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 -> {
|
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("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -422,20 +564,25 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
iterableDt.isSplitWordArray -> {
|
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)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -460,21 +607,25 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
iterableDt.isWordArray -> {
|
iterableDt.isWordArray -> {
|
||||||
val length = numElements * 2
|
val indexVar = if(asmgen.options.romable)
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
|
else
|
||||||
|
asmgen.makeLabel("for_index")
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -484,16 +635,16 @@ $loopLabel sty $indexVar
|
|||||||
lda $iterableName+1,y
|
lda $iterableName+1,y
|
||||||
sta $loopvarName+1""")
|
sta $loopvarName+1""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
if(length<=127) {
|
if(numElements<=127) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
iny
|
iny
|
||||||
cpy #$length
|
cpy #${numElements*2}
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
// length is 128 words, 256 bytes
|
// array size is 128 words, 256 bytes
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
@ -501,15 +652,17 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.StNodeType
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||||
@ -15,12 +16,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
// just ignore any result values from the function call.
|
// just ignore any result values from the function call.
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>) =
|
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>): Boolean {
|
||||||
when(params.size) {
|
// When the parameter(s) are passed via an explicit register or register pair,
|
||||||
1 -> params[0].type.isIntegerOrBool
|
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool
|
// Just load them in whatever the register spec says.
|
||||||
|
return when (params.size) {
|
||||||
|
1 -> params[0].type.isIntegerOrBool && params[0].register == null
|
||||||
|
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||||
// Output only the code to set up the parameters and perform the actual call
|
// Output only the code to set up the parameters and perform the actual call
|
||||||
@ -28,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||||
|
|
||||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||||
val sub = symbol?.astNode as IPtSubroutine
|
if(symbol.type == StNodeType.LABEL) {
|
||||||
|
require(call.void)
|
||||||
|
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||||
|
|
||||||
if(sub is PtAsmSub) {
|
if(sub is PtAsmSub) {
|
||||||
@ -47,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
if(bank==null) {
|
if(bank==null) {
|
||||||
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||||
if(varbank!=null) {
|
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) {
|
when(asmgen.options.compTarget.name) {
|
||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
// JSRFAR can jump to a banked RAM address as well!
|
// JSRFAR can jump to a banked RAM address as well!
|
||||||
@ -131,15 +146,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
useCpuRegistersForArgs(call.args, sub)
|
useCpuRegistersForArgs(call.args, sub)
|
||||||
} else {
|
} else {
|
||||||
// arguments via variables
|
// arguments via variables
|
||||||
val (normalParams, registerParams) = sub.parameters.withIndex().partition { it.value.register == null }
|
val paramValues = sub.parameters.zip(call.args)
|
||||||
|
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||||
if (normalParams.isNotEmpty()) {
|
if (normalParams.isNotEmpty()) {
|
||||||
for (arg in normalParams.zip(call.args))
|
for (p in normalParams)
|
||||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
argumentViaVariable(sub, p.first, p.second)
|
||||||
}
|
}
|
||||||
if (registerParams.isNotEmpty()) {
|
if (registerParams.isNotEmpty()) {
|
||||||
// the R0-R15 'registers' are not really registers. They're just special variables.
|
// the R0-R15 'registers' are not really registers. They're just special variables.
|
||||||
for (arg in registerParams.zip(call.args))
|
for (p in registerParams)
|
||||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
argumentViaVariable(sub, p.first, p.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subAsmName")
|
asmgen.out(" jsr $subAsmName")
|
||||||
@ -159,12 +175,22 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
2 -> {
|
2 -> {
|
||||||
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
|
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
|
||||||
// 2 byte params, second in Y, first in A
|
// 2 byte params, second in Y, first in A
|
||||||
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
if(asmgen.needAsaveForExpr(args[0]) && !asmgen.needAsaveForExpr(args[1])) {
|
||||||
if(asmgen.needAsaveForExpr(args[1]))
|
// first 0 then 1
|
||||||
asmgen.out(" pha")
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
if(asmgen.needAsaveForExpr(args[1]))
|
} else if(!asmgen.needAsaveForExpr(args[0]) && asmgen.needAsaveForExpr(args[1])) {
|
||||||
asmgen.out(" pla")
|
// first 1 then 0
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
|
if (asmgen.needAsaveForExpr(args[1]))
|
||||||
|
asmgen.out(" pha")
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
|
if (asmgen.needAsaveForExpr(args[1]))
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw AssemblyError("cannot use registers for word+byte")
|
throw AssemblyError("cannot use registers for word+byte")
|
||||||
}
|
}
|
||||||
@ -208,9 +234,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
val param = sub.parameters[it]
|
val param = sub.parameters[it]
|
||||||
val arg = call.args[it]
|
val arg = call.args[it]
|
||||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
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)
|
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}")
|
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -300,7 +326,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
register!!
|
register!!
|
||||||
if(requiredDt.largerSizeThan(value.type)) {
|
if(requiredDt.largerSizeThan(value.type)) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||||
} else {
|
} else {
|
||||||
@ -323,7 +349,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope)
|
asmgen.translateNormalAssignment(AsmAssignment(src, listOf(target), program.memsizer, target.position), scope)
|
||||||
}
|
}
|
||||||
return RegisterOrStatusflag(register, null)
|
return RegisterOrStatusflag(register, null)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,14 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
val compareCond = stmt.condition as? PtBinaryExpression
|
val compareCond = stmt.condition as? PtBinaryExpression
|
||||||
if(compareCond!=null) {
|
if(compareCond!=null) {
|
||||||
|
|
||||||
|
val useBIT = asmgen.checkIfConditionCanUseBIT(compareCond)
|
||||||
|
if(useBIT!=null) {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
val rightDt = compareCond.right.type
|
val rightDt = compareCond.right.type
|
||||||
return when {
|
return when {
|
||||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||||
@ -58,6 +66,67 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateIfBIT(ifElse: PtIfElse, jumpAfterIf: PtJump?, testForBitSet: Boolean, variable: PtIdentifier, bitmask: Int) {
|
||||||
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
|
|
||||||
|
fun branch(branchInstr: String, target: AsmGen6502Internal.JumpTarget) {
|
||||||
|
require(!target.needsExpressionEvaluation)
|
||||||
|
if(target.indirect)
|
||||||
|
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||||
|
if(ifElse.hasElse())
|
||||||
|
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||||
|
else
|
||||||
|
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bitmask) {
|
||||||
|
128 -> {
|
||||||
|
// test via bit + N flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
|
branch("bmi", target)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bpl", ifElse)
|
||||||
|
} else {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
|
branch("bpl", target)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bmi", ifElse)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
64 -> {
|
||||||
|
// test via bit + V flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
|
branch("bvs", target)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bvc", ifElse)
|
||||||
|
} else {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
|
branch("bvc", target)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bvs", ifElse)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("BIT only works for bits 6 and 7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkNotExtsubReturnsStatusReg(condition: PtExpression) {
|
private fun checkNotExtsubReturnsStatusReg(condition: PtExpression) {
|
||||||
val fcall = condition as? PtFunctionCall
|
val fcall = condition as? PtFunctionCall
|
||||||
@ -70,82 +139,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
|
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
|
||||||
val bittest = ifElse.condition as? PtBuiltinFunctionCall
|
|
||||||
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
|
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
|
||||||
|
|
||||||
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
|
|
||||||
val variable = bittest.args[0] as PtIdentifier
|
|
||||||
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
|
|
||||||
val testForBitSet = bittest.name.endsWith("_set")
|
|
||||||
when (bitnumber) {
|
|
||||||
7 -> {
|
|
||||||
// test via bit + N flag
|
|
||||||
asmgen.out(" bit ${variable.name}")
|
|
||||||
if(testForBitSet) {
|
|
||||||
if(jumpAfterIf!=null) {
|
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
|
||||||
require(!target.needsExpressionEvaluation)
|
|
||||||
if(target.indirect)
|
|
||||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
|
||||||
if(ifElse.hasElse())
|
|
||||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
|
||||||
else
|
|
||||||
asmgen.out(" bmi ${target.asmLabel}")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
translateIfElseBodies("bpl", ifElse)
|
|
||||||
} else {
|
|
||||||
if(jumpAfterIf!=null) {
|
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
|
||||||
require(!target.needsExpressionEvaluation)
|
|
||||||
if(target.indirect)
|
|
||||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
|
||||||
if(ifElse.hasElse())
|
|
||||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
|
||||||
else
|
|
||||||
asmgen.out(" bpl ${target.asmLabel}")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
translateIfElseBodies("bmi", ifElse)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
6 -> {
|
|
||||||
// test via bit + V flag
|
|
||||||
asmgen.out(" bit ${variable.name}")
|
|
||||||
if(testForBitSet) {
|
|
||||||
if(jumpAfterIf!=null) {
|
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
|
||||||
require(!target.needsExpressionEvaluation)
|
|
||||||
if(target.indirect)
|
|
||||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
|
||||||
if(ifElse.hasElse())
|
|
||||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
|
||||||
else
|
|
||||||
asmgen.out(" bvs ${target.asmLabel}")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
translateIfElseBodies("bvc", ifElse)
|
|
||||||
} else {
|
|
||||||
if(jumpAfterIf!=null) {
|
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
|
||||||
require(!target.needsExpressionEvaluation)
|
|
||||||
if(target.indirect)
|
|
||||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
|
||||||
if(ifElse.hasElse())
|
|
||||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
|
||||||
else
|
|
||||||
asmgen.out(" bvc ${target.asmLabel}")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
translateIfElseBodies("bvs", ifElse)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||||
asmgen.assignConditionValueToRegisterAndTest(ifElse.condition)
|
asmgen.assignConditionValueToRegisterAndTest(ifElse.condition)
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
@ -154,6 +149,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
translateIfElseBodies("beq", ifElse)
|
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) {
|
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||||
// comparison value is already in A
|
// comparison value is already in A
|
||||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||||
@ -162,7 +244,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -179,10 +261,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" $falseBranch +")
|
asmgen.out(" $falseBranch +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
asmgen.out("""
|
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||||
jmp (${target.asmLabel})
|
asmgen.out("+")
|
||||||
+""")
|
|
||||||
} else {
|
} else {
|
||||||
require(!target.needsExpressionEvaluation)
|
require(!target.needsExpressionEvaluation)
|
||||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||||
@ -218,40 +299,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
translateIfElseBodies("beq", stmt)
|
translateIfElseBodies("beq", stmt)
|
||||||
}
|
}
|
||||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||||
"<=" -> {
|
"<=" -> translateByteLessEqual(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||||
">=" -> {
|
">=" -> translateByteGreaterEqual(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in LogicalOperators -> {
|
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 (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||||
if (jumpAfterIf != null)
|
if (jumpAfterIf != null)
|
||||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||||
@ -294,7 +346,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bmi + | beq +")
|
asmgen.out(" bmi + | beq +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -314,7 +367,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -361,7 +414,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bmi + | bne ++")
|
asmgen.out(" bmi + | bne ++")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -383,7 +437,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
bpl $elseLabel
|
bpl $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -410,13 +464,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||||
val condition = stmt.condition as PtBinaryExpression
|
val condition = stmt.condition as PtBinaryExpression
|
||||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||||
asmgen.cmpAwithByteValue(condition.right, false)
|
|
||||||
if(signed) {
|
if(signed) {
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||||
else
|
else
|
||||||
translateIfElseBodies("bpl", stmt)
|
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||||
} else {
|
} else {
|
||||||
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||||
else
|
else
|
||||||
@ -424,25 +478,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?) {
|
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||||
val condition = stmt.condition as PtBinaryExpression
|
val condition = stmt.condition as PtBinaryExpression
|
||||||
if(signed) {
|
if(signed) {
|
||||||
// X>Y --> Y<X
|
// X>Y --> Y<X
|
||||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||||
asmgen.cmpAwithByteValue(condition.left, true)
|
|
||||||
if (jumpAfterIf != null)
|
if (jumpAfterIf != null)
|
||||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||||
else
|
else
|
||||||
translateIfElseBodies("bpl", stmt)
|
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||||
asmgen.cmpAwithByteValue(condition.right, false)
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bcc + | beq +")
|
asmgen.out(" bcc + | beq +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -461,7 +533,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -474,6 +546,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?) {
|
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||||
val signed = condition.left.type.isSigned
|
val signed = condition.left.type.isSigned
|
||||||
val constValue = condition.right.asConstInteger()
|
val constValue = condition.right.asConstInteger()
|
||||||
@ -543,7 +632,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
eor #128
|
eor #128
|
||||||
+ bpl +""")
|
+ bpl +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -570,7 +660,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
eor #128
|
eor #128
|
||||||
+ bmi $elseLabel""")
|
+ bmi $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -598,7 +688,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
cmp $valueLsb
|
cmp $valueLsb
|
||||||
bcs +""")
|
bcs +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
_jump jmp (${target.asmLabel})
|
_jump jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -623,7 +714,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcs $elseLabel""")
|
bcs $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -675,7 +766,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
eor #128
|
eor #128
|
||||||
+ bpl +""")
|
+ bpl +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -702,7 +794,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
eor #128
|
eor #128
|
||||||
+ bmi $elseLabel""")
|
+ bmi $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -729,7 +821,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcc +""")
|
bcc +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -752,7 +845,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcc $elseLabel""")
|
bcc $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -837,7 +930,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
lda $valueLsb
|
lda $valueLsb
|
||||||
bne ++""")
|
bne ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -865,7 +959,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
bne $elseLabel
|
bne $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -916,7 +1010,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #0
|
cmp #0
|
||||||
bne ++""")
|
bne ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -944,7 +1039,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
bne $elseLabel
|
bne $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -981,7 +1076,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
lda $valueLsb
|
lda $valueLsb
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1009,7 +1105,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1061,7 +1157,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #0
|
cmp #0
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1089,7 +1186,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1204,7 +1301,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1229,7 +1327,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1256,7 +1354,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1281,7 +1380,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1310,7 +1409,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1339,7 +1439,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1369,7 +1469,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1398,7 +1499,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1431,7 +1532,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1460,7 +1562,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1489,7 +1591,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1518,7 +1621,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,19 +1,7 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.ast.PtBinaryExpression
|
import prog8.code.ast.*
|
||||||
import prog8.code.ast.PtBuiltinFunctionCall
|
import prog8.code.core.*
|
||||||
import prog8.code.ast.PtExpression
|
|
||||||
import prog8.code.ast.PtIdentifier
|
|
||||||
import prog8.code.ast.PtIfExpression
|
|
||||||
import prog8.code.ast.PtNumber
|
|
||||||
import prog8.code.ast.PtPrefix
|
|
||||||
import prog8.code.core.AssemblyError
|
|
||||||
import prog8.code.core.BaseDataType
|
|
||||||
import prog8.code.core.CpuRegister
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.LogicalOperators
|
|
||||||
import prog8.code.core.RegisterOrPair
|
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
@ -21,24 +9,24 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
|||||||
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
||||||
|
|
||||||
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
||||||
require(target.datatype==expr.type)
|
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
|
||||||
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
||||||
val endLabel = asmgen.makeLabel("ifexpr_end")
|
val endLabel = asmgen.makeLabel("ifexpr_end")
|
||||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||||
when {
|
when {
|
||||||
expr.type.isByteOrBool -> {
|
expr.type.isByteOrBool -> {
|
||||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||||
asmgen.jmp(endLabel)
|
asmgen.jmp(endLabel)
|
||||||
asmgen.out(falseLabel)
|
asmgen.out(falseLabel)
|
||||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||||
}
|
}
|
||||||
expr.type.isWord -> {
|
expr.type.isWord || expr.type.isString -> {
|
||||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||||
asmgen.jmp(endLabel)
|
asmgen.jmp(endLabel)
|
||||||
asmgen.out(falseLabel)
|
asmgen.out(falseLabel)
|
||||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
@ -55,46 +43,26 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||||
if (condition is PtBinaryExpression) {
|
when (condition) {
|
||||||
val rightDt = condition.right.type
|
is PtBinaryExpression -> {
|
||||||
return when {
|
val rightDt = condition.right.type
|
||||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
return when {
|
||||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||||
else -> throw AssemblyError("weird dt")
|
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||||
}
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
|
||||||
else if(condition is PtPrefix && condition.operator=="not") {
|
|
||||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
|
||||||
} else {
|
|
||||||
// 'simple' condition, check if it is a byte bittest
|
|
||||||
val bittest = condition as? PtBuiltinFunctionCall
|
|
||||||
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
|
|
||||||
val variable = bittest.args[0] as PtIdentifier
|
|
||||||
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
|
|
||||||
val testForBitSet = bittest.name.endsWith("_set")
|
|
||||||
when (bitnumber) {
|
|
||||||
7 -> {
|
|
||||||
// test via bit + N flag
|
|
||||||
asmgen.out(" bit ${variable.name}")
|
|
||||||
if(testForBitSet) asmgen.out(" bpl $falseLabel")
|
|
||||||
else asmgen.out(" bmi $falseLabel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
6 -> {
|
|
||||||
// test via bit + V flag
|
|
||||||
asmgen.out(" bit ${variable.name}")
|
|
||||||
if(testForBitSet) asmgen.out(" bvc $falseLabel")
|
|
||||||
else asmgen.out(" bvs $falseLabel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
is PtPrefix if condition.operator=="not" -> {
|
||||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||||
asmgen.out(" beq $falseLabel")
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +87,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
asmgen.out(" beq $falseLabel")
|
asmgen.out(" beq $falseLabel")
|
||||||
}
|
}
|
||||||
in LogicalOperators -> {
|
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)) {
|
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||||
asmgen.out(" beq $falseLabel")
|
asmgen.out(" beq $falseLabel")
|
||||||
} else {
|
} else {
|
||||||
@ -227,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
beq $falseLabel
|
beq $falseLabel
|
||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp $varRight
|
cmp $varRight
|
||||||
bne +
|
bne +
|
||||||
@ -251,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
cmp $varRight+1
|
cmp $varRight+1
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp $varRight
|
cmp $varRight
|
||||||
bne $falseLabel
|
bne $falseLabel
|
||||||
@ -274,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
beq $falseLabel
|
beq $falseLabel
|
||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp #<$number
|
cmp #<$number
|
||||||
bne +
|
bne +
|
||||||
@ -297,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
cmp #>$number
|
cmp #>$number
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out( """
|
asmgen.out( """
|
||||||
cmp #<$number
|
cmp #<$number
|
||||||
bne $falseLabel
|
bne $falseLabel
|
||||||
@ -316,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
ora $varname+1
|
ora $varname+1
|
||||||
beq $falseLabel""")
|
beq $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -331,13 +299,37 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
ora $varname+1
|
ora $varname+1
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
||||||
// optimized code for byte comparisons with 0
|
// optimized code for byte comparisons with 0
|
||||||
|
|
||||||
|
val useBIT = asmgen.checkIfConditionCanUseBIT(condition)
|
||||||
|
if(useBIT!=null) {
|
||||||
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
|
val (testForBitSet, variable, bitmask) = useBIT
|
||||||
|
when (bitmask) {
|
||||||
|
128 -> {
|
||||||
|
// test via bit + N flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) asmgen.out(" bpl $falseLabel")
|
||||||
|
else asmgen.out(" bmi $falseLabel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
64 -> {
|
||||||
|
// test via bit + V flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) asmgen.out(" bvc $falseLabel")
|
||||||
|
else asmgen.out(" bvs $falseLabel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("BIT can only work on bits 7 and 6")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
asmgen.assignConditionValueToRegisterAndTest(condition.left)
|
asmgen.assignConditionValueToRegisterAndTest(condition.left)
|
||||||
when (condition.operator) {
|
when (condition.operator) {
|
||||||
"==" -> asmgen.out(" bne $falseLabel")
|
"==" -> asmgen.out(" bne $falseLabel")
|
||||||
|
@ -39,22 +39,21 @@ internal class ProgramAndVarsGen(
|
|||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
asmgen.out("; global float constants")
|
asmgen.out("; global float constants")
|
||||||
for (flt in allocator.globalFloatConsts) {
|
for (flt in allocator.globalFloatConsts) {
|
||||||
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key)
|
val floatFill = compTarget.getFloatAsmBytes(flt.key)
|
||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
|
|
||||||
memorySlabs()
|
memorySlabs()
|
||||||
tempVars()
|
|
||||||
footer()
|
footer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun header() {
|
private fun header() {
|
||||||
val ourName = this.javaClass.name
|
val ourName = this.javaClass.name
|
||||||
val cpu = when(compTarget.machine.cpu) {
|
val cpu = when(compTarget.cpu) {
|
||||||
CpuType.CPU6502 -> "6502"
|
CpuType.CPU6502 -> "6502"
|
||||||
CpuType.CPU65c02 -> "w65c02"
|
CpuType.CPU65C02 -> "w65c02"
|
||||||
else -> "unsupported"
|
else -> "unsupported"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,93 +86,121 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when(options.output) {
|
if(options.compTarget.customLauncher.isNotEmpty()) {
|
||||||
OutputType.RAW -> {
|
asmgen.out("; ---- custom launcher assembler program ----")
|
||||||
asmgen.out("; ---- raw assembler program ----")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
asmgen.out("prog8_program_start\t; start of program label")
|
for(line in options.compTarget.customLauncher) {
|
||||||
asmgen.out(" cld")
|
asmgen.out(line)
|
||||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
|
||||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
|
||||||
}
|
}
|
||||||
OutputType.PRG -> {
|
return
|
||||||
when(options.launcher) {
|
}
|
||||||
CbmPrgLauncherType.BASIC -> {
|
|
||||||
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
if(options.output == OutputType.LIBRARY) {
|
||||||
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
|
|
||||||
|
asmgen.out("; ---- library assembler program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" jmp p8b_main.p8s_start")
|
||||||
|
// note: the jmp above has 2 effects:
|
||||||
|
// 1. it prevents 64tass from stripping away all procs as unused code
|
||||||
|
// 2. it functions as the first entrypoint of the library, required anyway, to run the variable initialization/bss clear bootstrap code.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
when (options.output) {
|
||||||
|
OutputType.LIBRARY -> { }
|
||||||
|
OutputType.RAW -> {
|
||||||
|
asmgen.out("; ---- raw assembler program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
|
OutputType.PRG -> {
|
||||||
|
when (options.launcher) {
|
||||||
|
CbmPrgLauncherType.BASIC -> {
|
||||||
|
if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
|
||||||
|
errors.err(
|
||||||
|
"BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}",
|
||||||
|
program.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
asmgen.out("; ---- basic program with sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
val year = LocalDate.now().year
|
||||||
|
asmgen.out(" .word (+), $year")
|
||||||
|
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||||
|
asmgen.out("+\t.word 0")
|
||||||
|
asmgen.out("prog8_entrypoint")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
|
|
||||||
|
CbmPrgLauncherType.NONE -> {
|
||||||
|
// this is the same as RAW
|
||||||
|
asmgen.out("; ---- program without basic sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
}
|
}
|
||||||
asmgen.out("; ---- basic program with sys call ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
asmgen.out("prog8_program_start\t; start of program label")
|
|
||||||
val year = LocalDate.now().year
|
|
||||||
asmgen.out(" .word (+), $year")
|
|
||||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
|
||||||
asmgen.out("+\t.word 0")
|
|
||||||
asmgen.out("prog8_entrypoint")
|
|
||||||
asmgen.out(" cld")
|
|
||||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
|
||||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
|
||||||
}
|
|
||||||
CbmPrgLauncherType.NONE -> {
|
|
||||||
// this is the same as RAW
|
|
||||||
asmgen.out("; ---- program without basic sys call ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
asmgen.out("prog8_program_start\t; start of program label")
|
|
||||||
asmgen.out(" cld")
|
|
||||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
|
||||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OutputType.XEX -> {
|
||||||
|
asmgen.out("; ---- atari xex program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutputType.XEX -> {
|
|
||||||
asmgen.out("; ---- atari xex program ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
asmgen.out("prog8_program_start\t; start of program label")
|
|
||||||
asmgen.out(" cld")
|
|
||||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
|
||||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
|
||||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
if (options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
; zeropage is clobbered so we need to reset the machine at exit
|
; zeropage is clobbered so we need to reset the machine at exit
|
||||||
lda #>sys.reset_system
|
lda #>sys.reset_system
|
||||||
pha
|
pha
|
||||||
lda #<sys.reset_system
|
lda #<sys.reset_system
|
||||||
pha""")
|
pha""")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(compTarget.name) {
|
when (compTarget.name) {
|
||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
if(options.floats)
|
if (options.floats)
|
||||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||||
asmgen.out(" jsr p8b_main.p8s_start")
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
}
|
}
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
asmgen.out(" jsr p8b_main.p8s_start")
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
}
|
}
|
||||||
"c128" -> {
|
"c128" -> {
|
||||||
asmgen.out(" jsr p8b_main.p8s_start")
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.out(" jsr p8b_main.p8s_start")
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,27 +218,6 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.machine.FLOAT_MEM_SIZE}")
|
|
||||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asmgen.out(" .send BSS")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
var relocateBssVars = false
|
var relocateBssVars = false
|
||||||
var relocateBssSlabs = false
|
var relocateBssSlabs = false
|
||||||
@ -219,49 +225,49 @@ internal class ProgramAndVarsGen(
|
|||||||
var relocatedBssEnd = 0u
|
var relocatedBssEnd = 0u
|
||||||
|
|
||||||
if(options.varsGolden) {
|
if(options.varsGolden) {
|
||||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
relocateBssVars = true
|
relocateBssVars = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
|
||||||
}
|
}
|
||||||
else if(options.varsHighBank!=null) {
|
else if(options.varsHighBank!=null) {
|
||||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||||
throw AssemblyError("slabs and vars high bank must be the same")
|
throw AssemblyError("slabs and vars high bank must be the same")
|
||||||
relocateBssVars = true
|
relocateBssVars = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.slabsGolden) {
|
if(options.slabsGolden) {
|
||||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
relocateBssSlabs = true
|
relocateBssSlabs = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
|
||||||
}
|
}
|
||||||
else if(options.slabsHighBank!=null) {
|
else if(options.slabsHighBank!=null) {
|
||||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||||
throw AssemblyError("slabs and vars high bank must be the same")
|
throw AssemblyError("slabs and vars high bank must be the same")
|
||||||
relocateBssSlabs = true
|
relocateBssSlabs = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out("; bss sections")
|
asmgen.out("; bss sections")
|
||||||
@ -271,6 +277,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .dsection slabs_BSS")
|
asmgen.out(" .dsection slabs_BSS")
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
if(relocateBssSlabs)
|
if(relocateBssSlabs)
|
||||||
@ -278,6 +285,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
} else {
|
} else {
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
@ -290,6 +298,10 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(relocatedBssEnd >= options.memtopAddress)
|
||||||
|
options.memtopAddress = relocatedBssEnd+1u
|
||||||
|
|
||||||
asmgen.out(" ; memtop check")
|
asmgen.out(" ; memtop check")
|
||||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||||
}
|
}
|
||||||
@ -318,7 +330,9 @@ internal class ProgramAndVarsGen(
|
|||||||
initializers.forEach { assign ->
|
initializers.forEach { assign ->
|
||||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||||
asmgen.translate(assign)
|
asmgen.translate(assign)
|
||||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
else
|
||||||
|
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
|
||||||
|
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||||
}
|
}
|
||||||
asmgen.out(" rts\n .bend")
|
asmgen.out(" rts\n .bend")
|
||||||
}
|
}
|
||||||
@ -457,23 +471,23 @@ internal class ProgramAndVarsGen(
|
|||||||
sub.children.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
asmgen.out("; variables")
|
asmgen.out("; variables")
|
||||||
asmgen.out(" .section BSS")
|
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
asmgen.out(" .send BSS")
|
asmgen.out(" .send BSS_NOCLEAR")
|
||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
@ -601,15 +615,30 @@ internal class ProgramAndVarsGen(
|
|||||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||||
if(varsNoInit.isNotEmpty()) {
|
if(varsNoInit.isNotEmpty()) {
|
||||||
asmgen.out("; non-zeropage variables")
|
asmgen.out("; non-zeropage variables")
|
||||||
asmgen.out(" .section BSS")
|
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||||
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
|
|
||||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||||
uninitializedVariable2asm(it)
|
asmgen.out(" .section $section")
|
||||||
|
val (notAligned, aligned) = variables.partition { it.align == 0 }
|
||||||
|
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()) {
|
if(varsWithInit.isNotEmpty()) {
|
||||||
@ -624,6 +653,7 @@ internal class ProgramAndVarsGen(
|
|||||||
it.initializationStringValue!!.second,
|
it.initializationStringValue!!.second,
|
||||||
it.initializationStringValue!!.first
|
it.initializationStringValue!!.first
|
||||||
)
|
)
|
||||||
|
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 {
|
alignedStrings.sortedBy { it.align }.forEach {
|
||||||
outputStringvar(
|
outputStringvar(
|
||||||
@ -632,13 +662,22 @@ internal class ProgramAndVarsGen(
|
|||||||
it.initializationStringValue!!.second,
|
it.initializationStringValue!!.second,
|
||||||
it.initializationStringValue!!.first
|
it.initializationStringValue!!.first
|
||||||
)
|
)
|
||||||
|
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 {
|
notAlignedOther.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
|
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 {
|
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,7 +689,7 @@ internal class ProgramAndVarsGen(
|
|||||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
||||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
||||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
||||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||||
dt.isSplitWordArray -> {
|
dt.isSplitWordArray -> {
|
||||||
alignVar(variable.align)
|
alignVar(variable.align)
|
||||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||||
@ -674,28 +713,34 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||||
val initialValue: Number =
|
if(!variable.dt.isArray && !variable.dt.isString) {
|
||||||
if(variable.initializationNumericValue!=null) {
|
throw AssemblyError("static variables with an initialization value can only be an array or a string, not ${variable.dt} (${variable.name} ${variable.astNode?.position}")
|
||||||
if(variable.dt.isFloat)
|
// because numeric variables are in the BSS section and get initialized via assignment statements
|
||||||
variable.initializationNumericValue!!
|
}
|
||||||
else
|
|
||||||
variable.initializationNumericValue!!.toInt()
|
// val initialValue: Number =
|
||||||
} else 0
|
// if(variable.initializationNumericValue!=null) {
|
||||||
|
// if(variable.dt.isFloat)
|
||||||
|
// variable.initializationNumericValue!!
|
||||||
|
// else
|
||||||
|
// variable.initializationNumericValue!!.toInt()
|
||||||
|
// } else 0
|
||||||
|
//
|
||||||
|
|
||||||
val dt=variable.dt
|
val dt=variable.dt
|
||||||
when {
|
when {
|
||||||
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
// dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||||
dt.isFloat -> {
|
// dt.isFloat -> {
|
||||||
if(initialValue==0) {
|
// if(initialValue==0) {
|
||||||
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||||
} else {
|
// } else {
|
||||||
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
// val floatFill = compTarget.getFloatAsmBytes(initialValue)
|
||||||
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
// asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
dt.isString -> {
|
dt.isString -> {
|
||||||
throw AssemblyError("all string vars should have been interned into prog")
|
throw AssemblyError("all string vars should have been interned into prog")
|
||||||
}
|
}
|
||||||
@ -767,7 +812,7 @@ internal class ProgramAndVarsGen(
|
|||||||
dt.isFloatArray -> {
|
dt.isFloatArray -> {
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
compTarget.machine.getFloatAsmBytes(it.number!!)
|
compTarget.getFloatAsmBytes(it.number!!)
|
||||||
}
|
}
|
||||||
asmgen.out(varname)
|
asmgen.out(varname)
|
||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
|
@ -2,10 +2,7 @@ package prog8.codegen.cpu6502
|
|||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import com.github.michaelbull.result.onSuccess
|
import com.github.michaelbull.result.onSuccess
|
||||||
import prog8.code.StNode
|
import prog8.code.*
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
@ -14,7 +11,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
private val errors: IErrorReporter
|
private val errors: IErrorReporter
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val zeropage = options.compTarget.machine.zeropage
|
private val zeropage = options.compTarget.zeropage
|
||||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
||||||
|
|
||||||
@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
zeropageVars = zeropage.allocatedVariables
|
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 {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
val asmName = globalFloatConsts[number]
|
val asmName = globalFloatConsts[number]
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
|
|
||||||
import prog8.code.ast.PtBinaryExpression
|
import prog8.code.ast.PtBinaryExpression
|
||||||
import prog8.code.ast.PtExpression
|
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.C64Target
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
@ -50,17 +53,17 @@ internal class AnyExprAsmGen(
|
|||||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
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.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
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.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
@ -73,25 +76,25 @@ internal class AnyExprAsmGen(
|
|||||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||||
"or" -> TODO("logical or (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.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.out(" pla | and P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"|" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
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.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
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.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
@ -190,14 +193,14 @@ internal class AnyExprAsmGen(
|
|||||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||||
if (!right.isSimple()) asmgen.popFAC1()
|
if (!right.isSimple()) asmgen.popFAC1()
|
||||||
}
|
}
|
||||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
else -> TODO("don't know how to evaluate float expression for selected compilation target ${left.position}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
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()
|
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,14 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
VARIABLE,
|
VARIABLE,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
MEMORY,
|
MEMORY,
|
||||||
REGISTER
|
REGISTER,
|
||||||
|
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class SourceStorageKind {
|
internal enum class SourceStorageKind {
|
||||||
@ -49,6 +49,15 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||||
|
return targets.map {
|
||||||
|
if(it.void)
|
||||||
|
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||||
|
else
|
||||||
|
fromAstAssignment(it, definingSub, asmgen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||||
with(target) {
|
with(target) {
|
||||||
when {
|
when {
|
||||||
@ -78,18 +87,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> {
|
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)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> {
|
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)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> {
|
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.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
@ -107,7 +116,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> {
|
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)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,9 +139,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
left isSameAs memory!!
|
left isSameAs memory!!
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> false
|
||||||
false
|
TargetStorageKind.VOID -> false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +170,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
val bv = value as? PtBool
|
val bv = value as? PtBool
|
||||||
if(bv!=null)
|
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) {
|
return when(value) {
|
||||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||||
@ -183,7 +191,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||||
@ -194,9 +202,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
is PtFunctionCall -> {
|
is PtFunctionCall -> {
|
||||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||||
val sub = symbol.astNode as IPtSubroutine
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
val returnType =
|
||||||
|
if(sub is PtSub && sub.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")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -226,27 +237,30 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
|
|
||||||
|
|
||||||
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||||
val target: AsmAssignTarget,
|
val targets: List<AsmAssignTarget>,
|
||||||
val memsizer: IMemSizer,
|
val memsizer: IMemSizer,
|
||||||
val position: Position) {
|
val position: Position) {
|
||||||
init {
|
init {
|
||||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
targets.forEach { target ->
|
||||||
require(!source.datatype.isUndefined) { "must not be placeholder/undefined datatype at $position" }
|
if (!source.datatype.isArray && !source.datatype.isUndefined && !target.datatype.isArray && !target.datatype.isUndefined)
|
||||||
if(!source.datatype.isArray && !target.datatype.isArray)
|
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
||||||
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val target: AsmAssignTarget
|
||||||
|
get() = targets.single()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignment(source: AsmAssignSource,
|
internal class AsmAssignment(source: AsmAssignSource,
|
||||||
target: AsmAssignTarget,
|
targets: List<AsmAssignTarget>,
|
||||||
memsizer: IMemSizer,
|
memsizer: IMemSizer,
|
||||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
position: Position): AsmAssignmentBase(source, targets, memsizer, position)
|
||||||
|
|
||||||
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
||||||
val operator: String,
|
val operator: String,
|
||||||
target: AsmAssignTarget,
|
target: AsmAssignTarget,
|
||||||
memsizer: IMemSizer,
|
memsizer: IMemSizer,
|
||||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
position: Position): AsmAssignmentBase(source, listOf(target), memsizer, position)
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -68,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||||
|
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
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.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.StMemVar
|
||||||
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.source.SourceCode
|
import prog8.code.source.SourceCode
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502
|
import prog8.codegen.cpu6502.AsmGen6502
|
||||||
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
@ -26,8 +29,9 @@ class TestCodegen: FunSpec({
|
|||||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||||
floats = true,
|
floats = true,
|
||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -48,13 +52,14 @@ class TestCodegen: FunSpec({
|
|||||||
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"pi",
|
"pi",
|
||||||
DataType.forDt(BaseDataType.UBYTE),
|
DataType.UBYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
|
false,
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
@ -63,6 +68,7 @@ class TestCodegen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -72,29 +78,31 @@ class TestCodegen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"xx",
|
"xx",
|
||||||
DataType.forDt(BaseDataType.WORD),
|
DataType.WORD,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
|
false,
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
|
|
||||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
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(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
}
|
}
|
||||||
it.add(targetIdx)
|
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(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
assign.add(target)
|
assign.add(target)
|
||||||
@ -103,15 +111,15 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(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)
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(numberAssignTarget)
|
||||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||||
@ -119,10 +127,10 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(cxregAssignTarget)
|
||||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
sub.add(cxregAssign)
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
@ -130,7 +138,7 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
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)
|
program.add(cx16block)
|
||||||
|
|
||||||
val options = getTestOptions()
|
val options = getTestOptions()
|
||||||
@ -159,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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -6,11 +6,12 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":codeCore"))
|
implementation(project(":codeCore"))
|
||||||
|
implementation(project(":simpleAst"))
|
||||||
implementation(project(":intermediate"))
|
implementation(project(":intermediate"))
|
||||||
implementation(project(":codeGenIntermediate"))
|
implementation(project(":codeGenIntermediate"))
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -24,4 +25,4 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
// note: there are no unit tests in this module!
|
||||||
|
@ -13,5 +13,6 @@
|
|||||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||||
<orderEntry type="module" module-name="intermediate" />
|
<orderEntry type="module" module-name="intermediate" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
|
<orderEntry type="module" module-name="simpleAst" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.codegen.experimental
|
package prog8.codegen.experimental
|
||||||
|
|
||||||
|
import prog8.code.IAssemblyProgram
|
||||||
|
import prog8.code.ICodeGeneratorBackend
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.code.core.ICodeGeneratorBackend
|
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
@ -21,11 +21,13 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
// but you can also use the Intermediate Representation to build a codegen on:
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
val irProgram = irCodeGen.generate()
|
val irProgram = irCodeGen.generate()
|
||||||
|
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
|
||||||
|
|
||||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
|
|
||||||
println("** experimental codegen stub: no assembly generated **")
|
if(!options.quiet)
|
||||||
|
println("** experimental codegen stub: no assembly generated **")
|
||||||
return EmptyProgram
|
return EmptyProgram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
private object EmptyProgram : IAssemblyProgram {
|
private object EmptyProgram : IAssemblyProgram {
|
||||||
override val name = "<Empty Program>"
|
override val name = "<Empty Program>"
|
||||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
println("** nothing assembled **")
|
if(!options.quiet)
|
||||||
|
println("** nothing assembled **")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,11 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":codeCore"))
|
implementation(project(":codeCore"))
|
||||||
|
implementation(project(":simpleAst"))
|
||||||
implementation(project(":intermediate"))
|
implementation(project(":intermediate"))
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
|
|
||||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||||
@ -45,4 +46,4 @@ tasks.test {
|
|||||||
testLogging {
|
testLogging {
|
||||||
events("skipped", "failed")
|
events("skipped", "failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
|
<orderEntry type="module" module-name="simpleAst" />
|
||||||
<orderEntry type="module" module-name="intermediate" />
|
<orderEntry type="module" module-name="intermediate" />
|
||||||
<orderEntry type="library" name="io.kotest.assertions.core.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.runner.junit5.jvm" level="project" />
|
||||||
|
@ -2,6 +2,7 @@ package prog8.codegen.intermediate
|
|||||||
|
|
||||||
import prog8.code.StExtSub
|
import prog8.code.StExtSub
|
||||||
import prog8.code.StExtSubParameter
|
import prog8.code.StExtSubParameter
|
||||||
|
import prog8.code.StSub
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
@ -14,29 +15,48 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val values = assignment.value as? PtFunctionCall
|
val values = assignment.value as? PtFunctionCall
|
||||||
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
|
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
|
||||||
|
|
||||||
val sub = codeGen.symbolTable.lookup(values.name) as? StExtSub
|
|
||||||
?: throw AssemblyError("only asmsubs can return multiple values")
|
|
||||||
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val funcCall = this.expressionEval.translate(values)
|
val funcCall = expressionEval.translate(values)
|
||||||
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
|
|
||||||
if(funcCall.multipleResultFpRegs.isNotEmpty())
|
|
||||||
TODO("deal with (multiple?) FP return registers")
|
|
||||||
val assignmentTargets = assignment.children.dropLast(1)
|
val assignmentTargets = assignment.children.dropLast(1)
|
||||||
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
|
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
|
||||||
if(sub.returns.size==assignmentTargets.size) {
|
|
||||||
// Targets and values match. Assign all the things. Skip 'void' targets.
|
val extsub = codeGen.symbolTable.lookup(values.name) as? StExtSub
|
||||||
sub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
|
if(extsub!=null) {
|
||||||
val target = it.first.second as PtAssignTarget
|
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
|
||||||
if(!target.void) {
|
if (funcCall.multipleResultFpRegs.isNotEmpty())
|
||||||
val regNumber = it.second
|
TODO("deal with (multiple?) FP return registers")
|
||||||
val returns = it.first.first
|
if (extsub.returns.size == assignmentTargets.size) {
|
||||||
result += assignCpuRegister(returns, regNumber, target)
|
// Targets and values match. Assign all the things. Skip 'void' targets.
|
||||||
|
extsub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
|
||||||
|
val target = it.first.second as PtAssignTarget
|
||||||
|
if (!target.void) {
|
||||||
|
val regNumber = it.second
|
||||||
|
val returns = it.first.first
|
||||||
|
result += assignCpuRegister(returns, regNumber, target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("number of values and targets don't match")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw AssemblyError("number of values and targets don't match")
|
val normalsub = codeGen.symbolTable.lookup(values.name) as? StSub
|
||||||
|
if (normalsub != null) {
|
||||||
|
// 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)
|
||||||
|
val returnregs = (normalsub.astNode!! as IPtSubroutine).returnsWhatWhere()
|
||||||
|
normalsub.returns.zip(assignmentTargets).zip(returnregs).forEach {
|
||||||
|
val target = it.first.second as PtAssignTarget
|
||||||
|
if(!target.void) {
|
||||||
|
val reg = it.second.first
|
||||||
|
val regnum = codeGen.registers.next(irType(it.second.second))
|
||||||
|
val p = StExtSubParameter(reg, it.second.second)
|
||||||
|
result += assignCpuRegister(p, regnum, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw AssemblyError("expected extsub or normal sub")
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
if (assignment.target.children.single() is PtIrRegister)
|
if (assignment.target.children.single() is PtIrRegister)
|
||||||
@ -48,20 +68,22 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val loadCpuRegInstr = when(returns.register.registerOrPair) {
|
when(returns.register.registerOrPair) {
|
||||||
RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.X -> IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.Y -> IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.AX -> IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum), null)
|
||||||
RegisterOrPair.AY -> IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||||
null -> {
|
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||||
}
|
null -> if(returns.register.statusflag!=null)
|
||||||
else -> throw AssemblyError("cannot load register")
|
result += assignCpuStatusFlagReturnvalue(returns.register.statusflag!!, regNum)
|
||||||
|
else
|
||||||
|
throw AssemblyError("weird CPU register")
|
||||||
|
else -> throw AssemblyError("weird CPU register")
|
||||||
}
|
}
|
||||||
addInstr(result, loadCpuRegInstr, null)
|
|
||||||
|
|
||||||
// build an assignment to store the value in the actual target.
|
// build an assignment to store the value in the actual target.
|
||||||
val assign = PtAssignment(target.position)
|
val assign = PtAssignment(target.position)
|
||||||
@ -71,6 +93,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignCpuStatusFlagReturnvalue(statusflag: Statusflag, regNum: Int): IRCodeChunks {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
when(statusflag) {
|
||||||
|
Statusflag.Pc -> {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.ROXL, IRDataType.BYTE, reg1=regNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statusflag.Pz -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||||
|
Statusflag.Pn -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||||
|
Statusflag.Pv -> {
|
||||||
|
val skipLabel = codeGen.createLabelName()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.BSTVC, labelSymbol = skipLabel)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 1)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(skipLabel, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
// augmented assignment always has just a single target
|
// augmented assignment always has just a single target
|
||||||
if (augAssign.target.children.single() is PtIrRegister)
|
if (augAssign.target.children.single() is PtIrRegister)
|
||||||
@ -88,7 +134,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val chunks = when (augAssign.operator) {
|
val chunks = when (augAssign.operator) {
|
||||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
@ -98,12 +144,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||||
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"==" -> operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
|
||||||
"!=" -> operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
|
||||||
"<" -> operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
|
||||||
">" -> operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
|
||||||
"<=" -> operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
|
||||||
">=" -> operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
|
||||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||||
|
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||||
@ -125,7 +165,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
origAssign.operator.dropLast(1)
|
origAssign.operator.dropLast(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value = PtBinaryExpression(operator, origAssign.value.type, origAssign.value.position)
|
value = PtBinaryExpression(operator, origAssign.target.type, origAssign.value.position)
|
||||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||||
value.add(left)
|
value.add(left)
|
||||||
value.add(origAssign.value)
|
value.add(origAssign.value)
|
||||||
@ -145,7 +185,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(constAddress==null && memory!=null) {
|
if(constAddress==null && memory!=null) {
|
||||||
val register = codeGen.registers.nextFree()
|
val register = codeGen.registers.next(vmDt)
|
||||||
val tr = expressionEval.translateExpression(memory.address)
|
val tr = expressionEval.translateExpression(memory.address)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
when(operator) {
|
when(operator) {
|
||||||
@ -169,7 +209,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
"-" -> addInstr(result, IRInstruction(Opcode.NEGM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
"-" -> addInstr(result, IRInstruction(Opcode.NEGM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||||
"~" -> addInstr(result, IRInstruction(Opcode.INVM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
"~" -> addInstr(result, IRInstruction(Opcode.INVM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||||
"not" -> {
|
"not" -> {
|
||||||
val regMask = codeGen.registers.nextFree()
|
val regMask = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = 1)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = 1)
|
||||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = constAddress, labelSymbol = symbol)
|
it += IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = constAddress, labelSymbol = symbol)
|
||||||
@ -191,7 +231,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(array.index)
|
val tr = expressionEval.translateExpression(array.index)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
if(!array.splitWords && eltSize>1)
|
if(!array.splitWords && eltSize>1)
|
||||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||||
return tr.resultReg
|
return tr.resultReg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,8 +248,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
||||||
} else {
|
} else {
|
||||||
val indexReg = loadIndex()
|
val indexReg = loadIndex()
|
||||||
val registerLsb = codeGen.registers.nextFree()
|
val registerLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val registerMsb = codeGen.registers.nextFree()
|
val registerMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
||||||
@ -231,7 +271,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||||
} else {
|
} else {
|
||||||
val indexReg = loadIndex()
|
val indexReg = loadIndex()
|
||||||
val register = codeGen.registers.nextFree()
|
val register = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||||
@ -256,7 +296,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||||
} else {
|
} else {
|
||||||
val indexReg = loadIndex()
|
val indexReg = loadIndex()
|
||||||
val register = codeGen.registers.nextFree()
|
val register = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||||
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
||||||
@ -269,7 +309,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||||
} else {
|
} else {
|
||||||
val indexReg = loadIndex()
|
val indexReg = loadIndex()
|
||||||
val register = codeGen.registers.nextFree()
|
val register = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
||||||
@ -278,7 +318,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"not" -> {
|
"not" -> {
|
||||||
val register = codeGen.registers.nextFree()
|
val register = codeGen.registers.next(vmDt)
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
|
||||||
@ -327,7 +367,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if (assignment.value is PtIrRegister) {
|
if (assignment.value is PtIrRegister) {
|
||||||
valueRegister = (assignment.value as PtIrRegister).register
|
valueRegister = (assignment.value as PtIrRegister).register
|
||||||
if(extendByteToWord) {
|
if(extendByteToWord) {
|
||||||
valueRegister = codeGen.registers.nextFree()
|
valueRegister = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtIrRegister).register), null)
|
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtIrRegister).register), null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -335,7 +375,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
valueRegister = tr.resultReg
|
valueRegister = tr.resultReg
|
||||||
addToResult(result, tr, valueRegister, -1)
|
addToResult(result, tr, valueRegister, -1)
|
||||||
if(extendByteToWord) {
|
if(extendByteToWord) {
|
||||||
valueRegister = codeGen.registers.nextFree()
|
valueRegister = codeGen.registers.next(IRDataType.WORD)
|
||||||
val opcode = if(assignment.value.type.isSigned) Opcode.EXTS else Opcode.EXT
|
val opcode = if(assignment.value.type.isSigned) Opcode.EXTS else Opcode.EXT
|
||||||
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
|
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
|
||||||
}
|
}
|
||||||
@ -347,10 +387,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val instruction = if(zero) {
|
val instruction = if(zero) {
|
||||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||||
} else {
|
} else {
|
||||||
if (targetDt == IRDataType.FLOAT)
|
if (targetDt == IRDataType.FLOAT) {
|
||||||
|
require(valueFpRegister>=0)
|
||||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
|
require(valueRegister>=0)
|
||||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(null, null).also { it += instruction }
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
return result
|
return result
|
||||||
@ -403,10 +447,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val chunk = IRCodeChunk(null, null).also {
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
if(targetArray.splitWords) {
|
if(targetArray.splitWords) {
|
||||||
val msbReg = codeGen.registers.nextFree()
|
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||||
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||||
@ -417,10 +462,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
result += code
|
result += code
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
if(targetArray.splitWords) {
|
if(targetArray.splitWords) {
|
||||||
val msbReg = codeGen.registers.nextFree()
|
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
|
||||||
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
||||||
@ -455,7 +501,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||||
// STOREIX only works with byte index.
|
// STOREIX only works with byte index.
|
||||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||||
@ -498,7 +544,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
return Pair(result, tr.resultReg)
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
val mult: PtExpression = PtBinaryExpression("*", DataType.forDt(BaseDataType.UBYTE), array.position)
|
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
mult.children += array.index
|
mult.children += array.index
|
||||||
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
val tr = expressionEval.translateExpression(mult)
|
val tr = expressionEval.translateExpression(mult)
|
||||||
@ -514,16 +560,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(array.splitWords) {
|
if(array.splitWords) {
|
||||||
val valueRegLsb = codeGen.registers.nextFree()
|
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val valueRegMsb = codeGen.registers.nextFree()
|
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -531,7 +577,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace word array &")
|
return null // TODO("optimized inplace word array &")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place &")
|
return null // TODO("optimized memory in-place &")
|
||||||
@ -554,16 +600,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(array.splitWords) {
|
if(array.splitWords) {
|
||||||
val valueRegLsb = codeGen.registers.nextFree()
|
throw AssemblyError("logical and on (split) word array should not happen")
|
||||||
val valueRegMsb = codeGen.registers.nextFree()
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -571,7 +610,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace word array and")
|
return null // TODO("optimized inplace word array and")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place and")
|
return null // TODO("optimized memory in-place and")
|
||||||
@ -580,7 +619,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
if(!operand.isSimple()) {
|
if(!operand.isSimple()) {
|
||||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||||
val inplaceReg = codeGen.registers.nextFree()
|
val inplaceReg = codeGen.registers.next(vmDt)
|
||||||
val shortcutLabel = codeGen.createLabelName()
|
val shortcutLabel = codeGen.createLabelName()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += if(constAddress!=null)
|
it += if(constAddress!=null)
|
||||||
@ -614,16 +653,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(array.splitWords) {
|
if(array.splitWords) {
|
||||||
val valueRegLsb = codeGen.registers.nextFree()
|
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val valueRegMsb = codeGen.registers.nextFree()
|
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -631,7 +670,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace word array |")
|
return null // TODO("optimized inplace word array |")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place |")
|
return null // TODO("optimized memory in-place |")
|
||||||
@ -654,16 +693,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(array.splitWords) {
|
if(array.splitWords) {
|
||||||
val valueRegLsb = codeGen.registers.nextFree()
|
throw AssemblyError("logical or on (split) word array should not happen")
|
||||||
val valueRegMsb = codeGen.registers.nextFree()
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -671,7 +703,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace word array or")
|
return null // TODO("optimized inplace word array or")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place or"")
|
return null // TODO("optimized memory in-place or"")
|
||||||
@ -680,7 +712,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
if(!operand.isSimple()) {
|
if(!operand.isSimple()) {
|
||||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||||
val inplaceReg = codeGen.registers.nextFree()
|
val inplaceReg = codeGen.registers.next(vmDt)
|
||||||
val shortcutLabel = codeGen.createLabelName()
|
val shortcutLabel = codeGen.createLabelName()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += if(constAddress!=null)
|
it += if(constAddress!=null)
|
||||||
@ -761,7 +793,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||||
if(array!=null) {
|
if(array!=null) {
|
||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
@ -772,15 +804,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val constValue = operand.asConstInteger()
|
val constValue = operand.asConstInteger()
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(constValue!=1) {
|
if(constValue!=1) {
|
||||||
val valueReg=codeGen.registers.nextFree()
|
val valueReg=codeGen.registers.next(eltDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||||
it += IRInstruction(Opcode.MULM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||||
|
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace array * non-const")
|
return null // TODO("optimized inplace array * non-const")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place *"")
|
return null // TODO("optimized memory in-place *"")
|
||||||
@ -795,22 +828,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
addInstr(result, if(constAddress!=null)
|
addInstr(result, if(constAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||||
, null)
|
, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||||
val factor = constFactorRight.number.toInt()
|
val factor = constFactorRight.number.toInt()
|
||||||
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
|
result += codeGen.multiplyByConstInplace(vmDt, signed, constAddress, symbol, factor)
|
||||||
} else {
|
} else {
|
||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||||
addInstr(result, if(constAddress!=null)
|
addInstr(result, if(constAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = constAddress)
|
IRInstruction(opcode, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
IRInstruction(opcode, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||||
, null)
|
, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -830,7 +864,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(constValue==1) {
|
if(constValue==1) {
|
||||||
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||||
} else {
|
} else {
|
||||||
val valueReg=codeGen.registers.nextFree()
|
val valueReg=codeGen.registers.next(eltDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||||
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -838,7 +872,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace array -")
|
return null // TODO("optimized inplace array -")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place -"")
|
return null // TODO("optimized memory in-place -"")
|
||||||
@ -883,7 +917,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorMultiplyInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
private fun operatorMultiplyInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
||||||
return null // TODO("inplace split word array *")
|
return null // fallback to slow method // TODO("inplace split word array *")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorMinusInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
private fun operatorMinusInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
||||||
@ -893,7 +927,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val skip = codeGen.createLabelName()
|
val skip = codeGen.createLabelName()
|
||||||
if(constValue==1) {
|
if(constValue==1) {
|
||||||
val lsbReg = codeGen.registers.nextFree()
|
val lsbReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||||
@ -904,10 +938,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
return null // TODO("inplace split word array -")
|
return null // fallback to slow method // TODO("inplace split word array -")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null // TODO("inplace split word array -")
|
return null // fallback to slow method // TODO("inplace split word array -")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||||
@ -923,7 +957,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(constValue==1) {
|
if(constValue==1) {
|
||||||
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||||
} else {
|
} else {
|
||||||
val valueReg=codeGen.registers.nextFree()
|
val valueReg=codeGen.registers.next(elementDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
|
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
|
||||||
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -931,7 +965,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace array +")
|
return null // TODO("optimized inplace array +")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place +"")
|
return null // TODO("optimized memory in-place +"")
|
||||||
@ -990,18 +1024,46 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
result += IRCodeChunk(skip, null)
|
result += IRCodeChunk(skip, null)
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
return null // TODO("inplace split word array +")
|
return null // fallback to slow method // TODO("inplace split word array +")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null // TODO("inplace split word array +")
|
return null // fallback to slow method // TODO("inplace split word array +")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorShiftRightInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
private fun operatorShiftRightInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||||
if(array!=null) {
|
if(array!=null) {
|
||||||
TODO(">> in array")
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val constIndex = array.index.asConstInteger()
|
||||||
|
val constValue = operand.asConstInteger()
|
||||||
|
|
||||||
|
if(constIndex!=null && constValue!=null) {
|
||||||
|
if(array.splitWords) {
|
||||||
|
repeat(constValue) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||||
|
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
|
if(constValue==1) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
|
||||||
|
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return null // TODO("optimized >> in array")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place >>"")
|
return null // TODO("optimized memory in-place >>"")
|
||||||
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(codeGen.isOne(operand)) {
|
if(codeGen.isOne(operand)) {
|
||||||
@ -1026,7 +1088,35 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
private fun operatorShiftLeftInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
private fun operatorShiftLeftInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||||
if(array!=null) {
|
if(array!=null) {
|
||||||
TODO("<< in array")
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val constIndex = array.index.asConstInteger()
|
||||||
|
val constValue = operand.asConstInteger()
|
||||||
|
|
||||||
|
if(constIndex!=null && constValue!=null) {
|
||||||
|
if(array.splitWords) {
|
||||||
|
repeat(constValue) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
|
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
|
if(constValue==1) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val valueReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
|
||||||
|
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return null // TODO("optimized << in array")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place <<"")
|
return null // TODO("optimized memory in-place <<"")
|
||||||
@ -1058,16 +1148,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
if(constIndex!=null && constValue!=null) {
|
if(constIndex!=null && constValue!=null) {
|
||||||
if(array.splitWords) {
|
if(array.splitWords) {
|
||||||
val valueRegLsb = codeGen.registers.nextFree()
|
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val valueRegMsb = codeGen.registers.nextFree()
|
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(vmDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||||
@ -1075,7 +1165,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null // TODO("inplace word array xor")
|
return null // TODO("optimized inplace word array xor")
|
||||||
}
|
}
|
||||||
if(constAddress==null && memory!=null)
|
if(constAddress==null && memory!=null)
|
||||||
return null // TODO("optimized memory in-place xor"")
|
return null // TODO("optimized memory in-place xor"")
|
||||||
@ -1099,7 +1189,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return null // TODO("optimized memory in-place %"")
|
return null // TODO("optimized memory in-place %"")
|
||||||
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(vmDt)
|
||||||
if(operand is PtNumber) {
|
if(operand is PtNumber) {
|
||||||
val number = operand.number.toInt()
|
val number = operand.number.toInt()
|
||||||
if (constAddress != null) {
|
if (constAddress != null) {
|
||||||
@ -1138,293 +1228,4 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun operatorEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, "==")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SEQ)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SEQ)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun operatorNotEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, "!=")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SNE)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SNE)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun operatorGreaterInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, ">")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val opcode = if(signed) Opcode.SGTS else Opcode.SGT
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun operatorLessInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, "<")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val opcode = if(signed) Opcode.SLTS else Opcode.SLT
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun operatorGreaterEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, ">=")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val opcode = if(signed) Opcode.SGES else Opcode.SGE
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun operatorLessEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
|
||||||
if(array!=null)
|
|
||||||
return createInplaceArrayComparison(array, operand, "<=")
|
|
||||||
if(constAddress==null && memory!=null)
|
|
||||||
return null // TODO("optimized memory in-place compare"")
|
|
||||||
|
|
||||||
val opcode = if(signed) Opcode.SLES else Opcode.SLE
|
|
||||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
|
||||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
|
||||||
} else {
|
|
||||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createInplaceComparison(
|
|
||||||
constAddress: Int?,
|
|
||||||
symbol: String?,
|
|
||||||
vmDt: IRDataType,
|
|
||||||
operand: PtExpression,
|
|
||||||
compareAndSetOpcode: Opcode
|
|
||||||
): MutableList<IRCodeChunkBase> {
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
val cmpResultReg = codeGen.registers.nextFree()
|
|
||||||
if(operand is PtNumber || operand is PtBool) {
|
|
||||||
|
|
||||||
if(operand is PtNumber && operand.number==0.0 && compareAndSetOpcode in arrayOf(Opcode.SEQ, Opcode.SNE)) {
|
|
||||||
// ==0 or !=0 optimized case
|
|
||||||
val compareAndSetOpcodeZero = if(compareAndSetOpcode==Opcode.SEQ) Opcode.SZ else Opcode.SNZ
|
|
||||||
if (constAddress != null) {
|
|
||||||
// in-place modify a memory location
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
|
||||||
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1 = cmpResultReg, reg2 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in-place modify a symbol (variable)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
|
||||||
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1=cmpResultReg, reg2 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare against number that is not 0
|
|
||||||
val numberReg = codeGen.registers.nextFree()
|
|
||||||
val value = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
|
|
||||||
if (constAddress != null) {
|
|
||||||
// in-place modify a memory location
|
|
||||||
val innervalue = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = innervalue)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in-place modify a symbol (variable)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = value)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val tr = expressionEval.translateExpression(operand)
|
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
|
||||||
if (constAddress != null) {
|
|
||||||
// in-place modify a memory location
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in-place modify a symbol (variable)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createInplaceFloatComparison(
|
|
||||||
constAddress: Int?,
|
|
||||||
symbol: String?,
|
|
||||||
operand: PtExpression,
|
|
||||||
compareAndSetOpcode: Opcode
|
|
||||||
): MutableList<IRCodeChunkBase> {
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
|
||||||
val valueReg = codeGen.registers.nextFreeFloat()
|
|
||||||
val cmpReg = codeGen.registers.nextFree()
|
|
||||||
val zeroReg = codeGen.registers.nextFree()
|
|
||||||
if(operand is PtNumber) {
|
|
||||||
val numberReg = codeGen.registers.nextFreeFloat()
|
|
||||||
val cmpResultReg = codeGen.registers.nextFree()
|
|
||||||
if (constAddress != null) {
|
|
||||||
// in-place modify a memory location
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
|
|
||||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
|
||||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in-place modify a symbol (variable)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
|
|
||||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
|
||||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val tr = expressionEval.translateExpression(operand)
|
|
||||||
val cmpResultReg = codeGen.registers.nextFree()
|
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
|
||||||
if (constAddress != null) {
|
|
||||||
// in-place modify a memory location
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
|
||||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
|
||||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in-place modify a symbol (variable)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
|
||||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
|
||||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
|
||||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createInplaceArrayComparison(array: PtArrayIndexer, value: PtExpression, comparisonOperator: String): IRCodeChunks? {
|
|
||||||
if(array.type.isFloat)
|
|
||||||
return null // TODO("optimized in-place compare on float arrays"))
|
|
||||||
|
|
||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
|
||||||
if(array.splitWords)
|
|
||||||
TODO("inplace compare for split word array")
|
|
||||||
val vmDt = irType(array.type)
|
|
||||||
val constIndex = array.index.asConstInteger()
|
|
||||||
val constValue = value.asConstInteger()
|
|
||||||
val cmpResultReg = codeGen.registers.nextFree()
|
|
||||||
if(constIndex!=null) {
|
|
||||||
if(constValue==0) {
|
|
||||||
// comparison against zero.
|
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
|
||||||
it += when(comparisonOperator) {
|
|
||||||
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
|
||||||
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
|
||||||
"<" -> return null // TODO("array <0 inplace"))
|
|
||||||
"<=" -> return null // TODO("array <=0 inplace"))
|
|
||||||
">" -> return null // TODO("array >0 inplace"))
|
|
||||||
">=" -> return null // TODO("array >=0 inplace"))
|
|
||||||
else -> throw AssemblyError("invalid operator")
|
|
||||||
}
|
|
||||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
} else {
|
|
||||||
return null // TODO("compare against non-zero value"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(constValue==0) {
|
|
||||||
// comparison against zero.
|
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
val indexTr = expressionEval.translateExpression(array.index)
|
|
||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
|
|
||||||
it += when(comparisonOperator) {
|
|
||||||
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
|
||||||
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
|
||||||
"<" -> return null // TODO("array <0 inplace"))
|
|
||||||
"<=" -> return null // TODO("array <=0 inplace"))
|
|
||||||
">" -> return null // TODO("array >0 inplace"))
|
|
||||||
">=" -> return null // TODO("array >=0 inplace"))
|
|
||||||
else -> throw AssemblyError("invalid operator")
|
|
||||||
}
|
|
||||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return null // TODO("compare against non-zero value"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -3,6 +3,7 @@ package prog8.codegen.intermediate
|
|||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.AssemblyError
|
import prog8.code.core.AssemblyError
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.BaseDataType
|
||||||
|
import prog8.code.core.DataType
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
@ -42,8 +43,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
"ror" -> funcRolRor(call)
|
"ror" -> funcRolRor(call)
|
||||||
"rol2" -> funcRolRor(call)
|
"rol2" -> funcRolRor(call)
|
||||||
"ror2" -> funcRolRor(call)
|
"ror2" -> funcRolRor(call)
|
||||||
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
|
|
||||||
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
|
|
||||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||||
@ -56,12 +55,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val valueTr = exprGen.translateExpression(call.args[0])
|
val valueTr = exprGen.translateExpression(call.args[0])
|
||||||
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
||||||
return if(resultType==IRDataType.FLOAT) {
|
return if(resultType==IRDataType.FLOAT) {
|
||||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
|
||||||
ExpressionCodeResult(result, resultType, -1, resultFpReg)
|
ExpressionCodeResult(result, resultType, -1, resultFpReg)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(resultType)
|
||||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
|
||||||
ExpressionCodeResult(result, resultType, resultReg, -1)
|
ExpressionCodeResult(result, resultType, resultReg, -1)
|
||||||
}
|
}
|
||||||
@ -75,12 +74,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
return if(call.void)
|
return if(call.void)
|
||||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
else
|
else
|
||||||
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
|
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.next(IRDataType.WORD), -1) // TODO actually the result is returned in CPU registers AY...
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val bankTr = exprGen.translateExpression(call.args[0])
|
val bankTr = exprGen.translateExpression(call.args[0])
|
||||||
val addressTr = exprGen.translateExpression(call.args[1])
|
val addressTr = exprGen.translateExpression(call.args[1])
|
||||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||||
@ -93,7 +91,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
|
|
||||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val bankTr = exprGen.translateExpression(call.args[0])
|
val bankTr = exprGen.translateExpression(call.args[0])
|
||||||
val addressTr = exprGen.translateExpression(call.args[1])
|
val addressTr = exprGen.translateExpression(call.args[1])
|
||||||
val argumentA = exprGen.translateExpression(call.args[2])
|
val argumentA = exprGen.translateExpression(call.args[2])
|
||||||
@ -125,7 +122,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
|
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
|
||||||
divisionReg = tr.resultReg
|
divisionReg = tr.resultReg
|
||||||
remainderReg = codeGen.registers.nextFree()
|
remainderReg = codeGen.registers.next(type)
|
||||||
} else {
|
} else {
|
||||||
val numTr = exprGen.translateExpression(number)
|
val numTr = exprGen.translateExpression(number)
|
||||||
addToResult(result, numTr, numTr.resultReg, -1)
|
addToResult(result, numTr, numTr.resultReg, -1)
|
||||||
@ -145,13 +142,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
|
|
||||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
|
||||||
val left = exprGen.translateExpression(call.args[0])
|
val left = exprGen.translateExpression(call.args[0])
|
||||||
val right = exprGen.translateExpression(call.args[1])
|
val right = exprGen.translateExpression(call.args[1])
|
||||||
addToResult(result, left, left.resultReg, -1)
|
addToResult(result, left, left.resultReg, -1)
|
||||||
addToResult(result, right, right.resultReg, -1)
|
addToResult(result, right, right.resultReg, -1)
|
||||||
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
|
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to resultReg)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
@ -178,7 +175,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
when (sourceDt.base) {
|
when (sourceDt.base) {
|
||||||
BaseDataType.BYTE -> {
|
BaseDataType.BYTE -> {
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
val compareReg = codeGen.registers.nextFree()
|
val compareReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
||||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||||
@ -189,7 +186,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
}
|
}
|
||||||
BaseDataType.WORD -> {
|
BaseDataType.WORD -> {
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
val compareReg = codeGen.registers.nextFree()
|
val compareReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
||||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||||
@ -199,7 +196,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
||||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||||
}
|
}
|
||||||
@ -210,7 +207,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val tr = exprGen.translateExpression(call.args.single())
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(tr.dt)
|
||||||
|
|
||||||
if(tr.dt==IRDataType.FLOAT) {
|
if(tr.dt==IRDataType.FLOAT) {
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
@ -233,7 +230,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
when(dt.base) {
|
when(dt.base) {
|
||||||
BaseDataType.UBYTE -> {
|
BaseDataType.UBYTE -> {
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
|
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
|
||||||
}
|
}
|
||||||
@ -241,15 +238,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
}
|
}
|
||||||
BaseDataType.UWORD -> {
|
BaseDataType.UWORD -> {
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
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 -> {
|
BaseDataType.FLOAT -> {
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
|
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
|
||||||
}
|
}
|
||||||
@ -261,7 +258,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
|
|
||||||
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
if((call.args[0] as? PtNumber)?.number == 0.0) {
|
if((call.args[0] as? PtNumber)?.number == 0.0) {
|
||||||
// msb is 0, use EXT
|
// msb is 0, use EXT
|
||||||
val lsbTr = exprGen.translateExpression(call.args[1])
|
val lsbTr = exprGen.translateExpression(call.args[1])
|
||||||
@ -279,7 +276,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
|
|
||||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val type = irType(call.type)
|
val type = irType(call.type)
|
||||||
val valueTr = exprGen.translateExpression(call.args[0])
|
val valueTr = exprGen.translateExpression(call.args[0])
|
||||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||||
@ -406,7 +402,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
return if(dt==IRDataType.FLOAT) {
|
return if(dt==IRDataType.FLOAT) {
|
||||||
if(call.args[0] is PtNumber) {
|
if(call.args[0] is PtNumber) {
|
||||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
val resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
|
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
|
||||||
@ -415,7 +411,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
} else {
|
} else {
|
||||||
val tr = exprGen.translateExpression(call.args.single())
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
|
||||||
}
|
}
|
||||||
@ -423,7 +419,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(dt)
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
|
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
|
||||||
@ -432,7 +428,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
} else {
|
} else {
|
||||||
val tr = exprGen.translateExpression(call.args.single())
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(dt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
|
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
@ -474,7 +470,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if(address is PtNumber) {
|
return if(address is PtNumber) {
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val addressNum = address.number.toInt()
|
val addressNum = address.number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum)
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum)
|
||||||
@ -484,7 +480,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
} else {
|
} else {
|
||||||
val addressTr = exprGen.translateExpression(address)
|
val addressTr = exprGen.translateExpression(address)
|
||||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg)
|
||||||
}
|
}
|
||||||
@ -497,22 +493,26 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val name = (call.args[0] as PtString).value
|
val name = (call.args[0] as PtString).value
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
return exprGen.translateExpression(call.args.single())
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val tr = exprGen.translateExpression(call.args.single())
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
@ -545,10 +545,25 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val arr = (arg as? PtArrayIndexer)
|
val arr = (arg as? PtArrayIndexer)
|
||||||
val index = arr?.index?.asConstInteger()
|
val index = arr?.index?.asConstInteger()
|
||||||
if(arr!=null && index!=null) {
|
if(arr!=null && index!=null) {
|
||||||
if(arr.splitWords) TODO("IR rol/ror on split words array")
|
|
||||||
val variable = arr.variable.name
|
val variable = arr.variable.name
|
||||||
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
|
if(arr.splitWords) {
|
||||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
|
result += IRCodeChunk(null, null).also {
|
||||||
|
when(opcodeMemAndReg.first) {
|
||||||
|
Opcode.ROXRM, Opcode.RORM -> {
|
||||||
|
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
|
||||||
|
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
|
||||||
|
}
|
||||||
|
Opcode.ROXLM, Opcode.ROLM -> {
|
||||||
|
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
|
||||||
|
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("wrong rol/ror opcode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
|
||||||
|
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
|
||||||
|
}
|
||||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,7 +592,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
if(isConstZeroValue) {
|
if(isConstZeroValue) {
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val pointerReg = codeGen.registers.nextFree()
|
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||||
if (msb)
|
if (msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||||
@ -587,7 +602,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val valueTr = exprGen.translateExpression(call.args[1])
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val pointerReg = codeGen.registers.nextFree()
|
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||||
if (msb)
|
if (msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||||
@ -602,7 +617,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||||
if(isConstZeroValue) {
|
if(isConstZeroValue) {
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
|
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
|
||||||
@ -618,7 +633,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val valueTr = exprGen.translateExpression(call.args[1])
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
|
||||||
@ -637,7 +652,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val constIndex = target.index.asConstInteger()
|
val constIndex = target.index.asConstInteger()
|
||||||
if(isConstZeroValue) {
|
if(isConstZeroValue) {
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||||
@ -648,7 +663,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||||
if(msb)
|
if(msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
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 = target.variable.name)
|
||||||
@ -658,7 +673,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
val valueTr = exprGen.translateExpression(call.args[1])
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||||
@ -669,7 +684,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||||
if(msb)
|
if(msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
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 = target.variable.name)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.StNode
|
import prog8.code.*
|
||||||
import prog8.code.StExtSub
|
|
||||||
import prog8.code.StSub
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
@ -29,11 +27,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is PtIrRegister -> {
|
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 -> {
|
is PtBool -> {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultRegister, immediate = expr.asInt())
|
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultRegister, immediate = expr.asInt())
|
||||||
ExpressionCodeResult(code, IRDataType.BYTE, resultRegister, -1)
|
ExpressionCodeResult(code, IRDataType.BYTE, resultRegister, -1)
|
||||||
}
|
}
|
||||||
@ -41,12 +42,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val vmDt = irType(expr.type)
|
val vmDt = irType(expr.type)
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
val resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number)
|
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number)
|
||||||
ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
|
ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(vmDt)
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = expr.number.toInt())
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = expr.number.toInt())
|
||||||
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
||||||
}
|
}
|
||||||
@ -56,12 +57,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if (expr.type.isPassByValue) {
|
if (expr.type.isPassByValue) {
|
||||||
val vmDt = irType(expr.type)
|
val vmDt = irType(expr.type)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
val resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name)
|
code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name)
|
||||||
ExpressionCodeResult(code, vmDt, -1, resultFpRegister)
|
ExpressionCodeResult(code, vmDt, -1, resultFpRegister)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(vmDt)
|
||||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
|
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
|
||||||
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
||||||
}
|
}
|
||||||
@ -69,7 +70,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
// for strings and arrays etc., load the *address* of the value instead
|
// for strings and arrays etc., load the *address* of the value instead
|
||||||
// for arrays this could mean a split word array, in which case we take the address of the _lsb array which comes first
|
// for arrays this could mean a split word array, in which case we take the address of the _lsb array which comes first
|
||||||
val vmDt = if(expr.type.isUndefined) IRDataType.WORD else irType(expr.type)
|
val vmDt = if(expr.type.isUndefined) IRDataType.WORD else irType(expr.type)
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(vmDt)
|
||||||
val labelsymbol = if(expr.type.isSplitWordArray) expr.name+"_lsb" else expr.name
|
val labelsymbol = if(expr.type.isSplitWordArray) expr.name+"_lsb" else expr.name
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = labelsymbol)
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = labelsymbol)
|
||||||
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
||||||
@ -96,15 +97,46 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
||||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||||
|
|
||||||
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val condTr = translateExpression(ifExpr.condition)
|
|
||||||
val trueTr = translateExpression(ifExpr.truevalue)
|
val trueTr = translateExpression(ifExpr.truevalue)
|
||||||
val falseTr = translateExpression(ifExpr.falsevalue)
|
val falseTr = translateExpression(ifExpr.falsevalue)
|
||||||
val irDt = irType(ifExpr.type)
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
|
||||||
val falseLabel = codeGen.createLabelName()
|
val falseLabel = codeGen.createLabelName()
|
||||||
val endLabel = codeGen.createLabelName()
|
val endLabel = codeGen.createLabelName()
|
||||||
|
val irDt = irType(ifExpr.type)
|
||||||
|
|
||||||
|
if(ifExpr.condition is PtBinaryExpression) {
|
||||||
|
val useBIT = checkIfConditionCanUseBIT(ifExpr.condition as PtBinaryExpression)
|
||||||
|
if(useBIT!=null) {
|
||||||
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
|
val (testBitSet, variable, bitmask) = useBIT
|
||||||
|
val bitBranchOpcode = when(testBitSet) {
|
||||||
|
true -> when(bitmask) {
|
||||||
|
64 -> Opcode.BSTVC
|
||||||
|
128 -> Opcode.BSTPOS
|
||||||
|
else -> throw AssemblyError("need bit 6 or 7")
|
||||||
|
}
|
||||||
|
false -> when(bitmask) {
|
||||||
|
64 -> Opcode.BSTVS
|
||||||
|
128 -> Opcode.BSTNEG
|
||||||
|
else -> throw AssemblyError("need bit 6 or 7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = variable.name)
|
||||||
|
it += IRInstruction(bitBranchOpcode, labelSymbol = falseLabel)
|
||||||
|
}
|
||||||
|
addToResult(result, trueTr, trueTr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||||
|
result += IRCodeChunk(falseLabel, null)
|
||||||
|
addToResult(result, falseTr, trueTr.resultReg, -1)
|
||||||
|
result += IRCodeChunk(endLabel, null)
|
||||||
|
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
|
||||||
|
val condTr = translateExpression(ifExpr.condition)
|
||||||
addToResult(result, condTr, condTr.resultReg, -1)
|
addToResult(result, condTr, condTr.resultReg, -1)
|
||||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=condTr.resultReg, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=condTr.resultReg, immediate = 0), null)
|
||||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = falseLabel), null)
|
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = falseLabel), null)
|
||||||
@ -131,46 +163,55 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val symbol = expr.identifier.name
|
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
|
// 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 result = mutableListOf<IRCodeChunkBase>()
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(vmDt)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// the _lsb split array comes first in memory
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||||
|
} else
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||||
|
}
|
||||||
|
|
||||||
if(expr.isFromArrayElement) {
|
if(expr.isFromArrayElement) {
|
||||||
if(expr.identifier.type.isSplitWordArray)
|
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||||
TODO("address of element of a split word array")
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
|
val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
|
||||||
val indexTr2 = translateExpression(expr.arrayIndexExpr!!)
|
val ixWord = codeGen.registers.next(IRDataType.WORD)
|
||||||
addToResult(result, indexTr2, indexTr2.resultReg, -1)
|
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
|
||||||
val indexWordReg = codeGen.registers.nextFree()
|
ixWord
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr2.resultReg), null)
|
} else indexTr.resultReg
|
||||||
if(expr.identifier.type.isUnsignedWord) {
|
if(expr.identifier.type.isUnsignedWord) {
|
||||||
|
require(!expr.isMsbForSplitArray)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
val ptr = codeGen.symbolTable.lookup(expr.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 = symbol)
|
||||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
// multiply indexTr resultreg by the eltSize and add this to the resultRegister.
|
loadAddressOfArrayLabel(resultRegister)
|
||||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||||
if(eltSize>1) {
|
|
||||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||||
}
|
}
|
||||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(expr.isMsbForSplitArray) {
|
loadAddressOfArrayLabel(resultRegister)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_msb"), null)
|
|
||||||
} else if(expr.identifier.type.isSplitWordArray) {
|
|
||||||
// the _lsb split array comes first in memory
|
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_lsb"), null)
|
|
||||||
} else
|
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
|
|
||||||
}
|
}
|
||||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
|
||||||
val constAddress = mem.address as? PtNumber
|
val constAddress = mem.address as? PtNumber
|
||||||
if(constAddress!=null) {
|
if(constAddress!=null) {
|
||||||
@ -183,7 +224,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||||
// LOADIX only works with byte index.
|
// LOADIX only works with byte index.
|
||||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||||
val offsetReg = codeGen.registers.nextFree()
|
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||||
@ -228,17 +269,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val endLabel = codeGen.createLabelName()
|
val endLabel = codeGen.createLabelName()
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
|
val boolResultRegister = if(elementDt.isByteOrBool) elementTr.resultReg else codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
for(value in haystack){
|
for(value in haystack){
|
||||||
it += IRInstruction(Opcode.CMPI, irType(elementDt), elementTr.resultReg, immediate = value)
|
it += IRInstruction(Opcode.CMPI, irType(elementDt), elementTr.resultReg, immediate = value)
|
||||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = gottemLabel)
|
it += IRInstruction(Opcode.BSTEQ, labelSymbol = gottemLabel)
|
||||||
}
|
}
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 0)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, boolResultRegister, immediate = 0)
|
||||||
it += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
it += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 1), gottemLabel)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, boolResultRegister, immediate = 1), gottemLabel)
|
||||||
result += IRCodeChunk(endLabel, null)
|
result += IRCodeChunk(endLabel, null)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, boolResultRegister, -1)
|
||||||
}
|
}
|
||||||
elementDt.isFloat -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
|
elementDt.isFloat -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
|
||||||
else -> throw AssemblyError("weird dt $elementDt")
|
else -> throw AssemblyError("weird dt $elementDt")
|
||||||
@ -248,49 +290,48 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val haystackVar = check.haystackHeapVar!!
|
val haystackVar = check.haystackHeapVar!!
|
||||||
when {
|
when {
|
||||||
haystackVar.type.isString -> {
|
haystackVar.type.isString -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||||
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to resultReg)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isByteArray -> {
|
haystackVar.type.isByteArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||||
val lengthReg = codeGen.registers.nextFree()
|
val lengthReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||||
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isWordArray -> {
|
haystackVar.type.isWordArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||||
val lengthReg = codeGen.registers.nextFree()
|
val lengthReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||||
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isFloatArray -> {
|
haystackVar.type.isFloatArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||||
val lengthReg = codeGen.registers.nextFree()
|
val lengthReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||||
result += codeGen.makeSyscall(IMSyscall.FLOATARRAY_CONTAINS, listOf(IRDataType.FLOAT to elementTr.resultFpReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
|
result += codeGen.makeSyscall(IMSyscall.FLOATARRAY_CONTAINS, listOf(IRDataType.FLOAT to elementTr.resultFpReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
|
||||||
@ -310,12 +351,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
|
|
||||||
if(arrayIx.splitWords) {
|
if(arrayIx.splitWords) {
|
||||||
require(vmDt==IRDataType.WORD)
|
require(vmDt==IRDataType.WORD)
|
||||||
resultRegister = codeGen.registers.nextFree()
|
resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val finalResultReg = codeGen.registers.nextFree()
|
val finalResultReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
if(arrayIx.index is PtNumber) {
|
if(arrayIx.index is PtNumber) {
|
||||||
val memOffset = (arrayIx.index as PtNumber).number.toInt()
|
val memOffset = (arrayIx.index as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val tmpRegMsb = codeGen.registers.nextFree()
|
val tmpRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, labelSymbol= "${arrayVarSymbol}_msb", symbolOffset = memOffset)
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, labelSymbol= "${arrayVarSymbol}_msb", symbolOffset = memOffset)
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, labelSymbol= "${arrayVarSymbol}_lsb", symbolOffset = memOffset)
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, labelSymbol= "${arrayVarSymbol}_lsb", symbolOffset = memOffset)
|
||||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
||||||
@ -324,7 +365,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(arrayIx.index)
|
val tr = translateExpression(arrayIx.index)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val tmpRegMsb = codeGen.registers.nextFree()
|
val tmpRegMsb = codeGen.registers.next(IRDataType.BYTE)
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_msb")
|
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_msb")
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_lsb")
|
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_lsb")
|
||||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
||||||
@ -337,24 +378,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if(arrayIx.index is PtNumber) {
|
if(arrayIx.index is PtNumber) {
|
||||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.nextFreeFloat()
|
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultRegister = codeGen.registers.nextFree()
|
resultRegister = codeGen.registers.next(vmDt)
|
||||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val tr = translateExpression(arrayIx.index)
|
val tr = translateExpression(arrayIx.index)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.nextFreeFloat()
|
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=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultRegister = codeGen.registers.nextFree()
|
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=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -398,15 +439,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
BaseDataType.BOOL -> {
|
BaseDataType.BOOL -> {
|
||||||
when {
|
when {
|
||||||
valueDt.isByte -> {
|
valueDt.isByte -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
|
||||||
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg), null)
|
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||||
}
|
}
|
||||||
valueDt.isWord -> {
|
valueDt.isWord -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||||
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg), null)
|
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||||
}
|
}
|
||||||
valueDt.isFloat -> {
|
valueDt.isFloat -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SGN, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg)
|
it += IRInstruction(Opcode.SGN, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg)
|
||||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
|
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
|
||||||
@ -417,11 +458,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
BaseDataType.UBYTE -> {
|
BaseDataType.UBYTE -> {
|
||||||
when(valueDt.base) {
|
when(valueDt.base) {
|
||||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
|
BaseDataType.BOOL, BaseDataType.BYTE -> {
|
||||||
actualResultReg2 = tr.resultReg // just keep the LSB as it is
|
actualResultReg2 = tr.resultReg
|
||||||
|
}
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> {
|
||||||
|
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
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")
|
||||||
@ -429,11 +474,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
BaseDataType.BYTE -> {
|
BaseDataType.BYTE -> {
|
||||||
when(valueDt.base) {
|
when(valueDt.base) {
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
|
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
||||||
actualResultReg2 = tr.resultReg // just keep the LSB as it is
|
actualResultReg2 = tr.resultReg
|
||||||
|
}
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> {
|
||||||
|
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
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")
|
||||||
@ -443,19 +492,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
when(valueDt.base) {
|
when(valueDt.base) {
|
||||||
BaseDataType.BYTE -> {
|
BaseDataType.BYTE -> {
|
||||||
// byte -> uword: sign extend
|
// byte -> uword: sign extend
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
|
||||||
}
|
}
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
||||||
// ubyte -> uword: sign extend
|
// ubyte -> uword: sign extend
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
|
||||||
}
|
}
|
||||||
BaseDataType.WORD -> {
|
BaseDataType.WORD -> {
|
||||||
actualResultReg2 = tr.resultReg
|
actualResultReg2 = tr.resultReg
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird cast value type")
|
else -> throw AssemblyError("weird cast value type")
|
||||||
@ -465,26 +514,26 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
when(valueDt.base) {
|
when(valueDt.base) {
|
||||||
BaseDataType.BYTE -> {
|
BaseDataType.BYTE -> {
|
||||||
// byte -> word: sign extend
|
// byte -> word: sign extend
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||||
}
|
}
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
||||||
// byte -> word: sign extend
|
// byte -> word: sign extend
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||||
}
|
}
|
||||||
BaseDataType.UWORD -> {
|
BaseDataType.UWORD -> {
|
||||||
actualResultReg2 = tr.resultReg
|
actualResultReg2 = tr.resultReg
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
actualResultReg2 = codeGen.registers.nextFree()
|
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BaseDataType.FLOAT -> {
|
BaseDataType.FLOAT -> {
|
||||||
actualResultFpReg2 = codeGen.registers.nextFreeFloat()
|
actualResultFpReg2 = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
when(valueDt.base) {
|
when(valueDt.base) {
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
BaseDataType.BOOL, BaseDataType.UBYTE -> {
|
||||||
addInstr(result, IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
addInstr(result, IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||||
@ -513,8 +562,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
return when(binExpr.operator) {
|
return when(binExpr.operator) {
|
||||||
"+" -> operatorPlus(binExpr, vmDt)
|
"+" -> operatorPlus(binExpr, vmDt)
|
||||||
"-" -> operatorMinus(binExpr, vmDt)
|
"-" -> operatorMinus(binExpr, vmDt)
|
||||||
"*" -> operatorMultiply(binExpr, vmDt)
|
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||||
"/" -> operatorDivide(binExpr, vmDt, signed)
|
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||||
"%" -> operatorModulo(binExpr, vmDt)
|
"%" -> operatorModulo(binExpr, vmDt)
|
||||||
"|" -> operatorOr(binExpr, vmDt, true)
|
"|" -> operatorOr(binExpr, vmDt, true)
|
||||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||||
@ -533,8 +582,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||||
val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)
|
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.scopedName 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.
|
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
|
||||||
@ -566,7 +615,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
when (callTarget) {
|
when (callTarget) {
|
||||||
is StSub -> {
|
is StSub -> {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
|
||||||
// assign the arguments
|
// assign the arguments
|
||||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||||
@ -589,27 +637,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
result += codeGen.translateNode(assign)
|
result += codeGen.translateNode(assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// return value (always singular for normal Subs)
|
// return value(s)
|
||||||
val returnRegSpec = if(fcall.void) null else {
|
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||||
val returnIrType = irType(callTarget.returnType!!)
|
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
|
||||||
if(returnIrType==IRDataType.FLOAT)
|
// So we use an empty list to avoid confusion here. This may change in a future version.
|
||||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
|
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
|
||||||
else
|
callTarget.returns.map {
|
||||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
|
val returnIrType = irType(it)
|
||||||
|
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// create the call
|
// create the call
|
||||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name,
|
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name,
|
||||||
fcallArgs = FunctionCallArgs(argRegisters, if(returnRegSpec==null) emptyList() else listOf(returnRegSpec))), null)
|
fcallArgs = FunctionCallArgs(argRegisters, returnRegSpecs)), null)
|
||||||
return if(fcall.void)
|
return if(fcall.void)
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) // TODO datatype void?
|
||||||
|
else if(returnRegSpecs.size==1) {
|
||||||
|
val returnRegSpec = returnRegSpecs.single()
|
||||||
|
if (fcall.type.isFloat)
|
||||||
|
ExpressionCodeResult(result, returnRegSpec.dt, -1, returnRegSpec.registerNum)
|
||||||
|
else
|
||||||
|
ExpressionCodeResult(result, returnRegSpec.dt, returnRegSpec.registerNum, -1)
|
||||||
|
} else {
|
||||||
|
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||||
|
// so the actual result of the expression here is 'void' (doesn't use IR virtual registers at all)
|
||||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
else if(fcall.type.isFloat)
|
}
|
||||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
|
||||||
else
|
|
||||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
|
||||||
}
|
}
|
||||||
is StExtSub -> {
|
is StExtSub -> {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
|
||||||
// assign the arguments
|
// assign the arguments
|
||||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||||
@ -620,24 +676,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
else
|
else
|
||||||
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
|
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
|
||||||
result += tr.chunks
|
result += tr.chunks
|
||||||
when(parameter.register.registerOrPair) {
|
result += codeGen.setCpuRegister(parameter.register, paramDt, tr.resultReg, tr.resultFpReg)
|
||||||
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=tr.resultReg), null)
|
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> TODO("floating point register parameters not supported")
|
|
||||||
in Cx16VirtualRegisters -> {
|
|
||||||
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1=tr.resultReg, labelSymbol = "cx16.${parameter.register.registerOrPair.toString().lowercase()}"), null)
|
|
||||||
}
|
|
||||||
null -> when(parameter.register.statusflag) {
|
|
||||||
// TODO: do the statusflag argument as last
|
|
||||||
Statusflag.Pc -> addInstr(result, IRInstruction(Opcode.LSR, paramDt, reg1=tr.resultReg), null)
|
|
||||||
else -> throw AssemblyError("weird statusflag as param")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("unsupported register arg")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(callTarget.returns.size>1)
|
if(callTarget.returns.size>1)
|
||||||
@ -650,12 +689,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
else {
|
else {
|
||||||
val returns = callTarget.returns[0]
|
val returns = callTarget.returns[0]
|
||||||
val returnIrType = irType(returns.type)
|
val returnIrType = irType(returns.type)
|
||||||
if (returnIrType == IRDataType.FLOAT)
|
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), returns.register)
|
||||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
|
|
||||||
else {
|
|
||||||
val returnRegister = codeGen.registers.nextFree()
|
|
||||||
FunctionCallArgs.RegSpec(returnIrType, returnRegister, returns.register)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create the call
|
// create the call
|
||||||
@ -670,9 +704,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
Opcode.CALL,
|
Opcode.CALL,
|
||||||
address = address.address.toInt(),
|
address = address.address.toInt(),
|
||||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||||
}
|
} else if(address.constbank!=null) {
|
||||||
else {
|
IRInstruction(
|
||||||
TODO("callfar into another bank is not implemented for the selected compilation target")
|
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)
|
addInstr(result, call, null)
|
||||||
@ -685,10 +731,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
// assign status flag bit to the return value register
|
// assign status flag bit to the return value register
|
||||||
finalReturnRegister = returnRegSpec.registerNum
|
finalReturnRegister = returnRegSpec.registerNum
|
||||||
if(finalReturnRegister<0)
|
if(finalReturnRegister<0)
|
||||||
finalReturnRegister = codeGen.registers.nextFree()
|
finalReturnRegister = codeGen.registers.next(returnRegSpec.dt)
|
||||||
when(statusFlagResult) {
|
when(statusFlagResult) {
|
||||||
Statusflag.Pc -> {
|
Statusflag.Pc -> {
|
||||||
addInstr(result, IRInstruction(Opcode.SCS, returnRegSpec.dt, reg1=finalReturnRegister), null)
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOAD, returnRegSpec.dt, reg1 = finalReturnRegister, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.ROXL, returnRegSpec.dt, reg1 = finalReturnRegister)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val branchOpcode = when(statusFlagResult) {
|
val branchOpcode = when(statusFlagResult) {
|
||||||
@ -720,10 +769,46 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
else
|
else
|
||||||
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid node type")
|
else -> {
|
||||||
|
if(callTarget.type == StNodeType.LABEL) {
|
||||||
|
require(fcall.void)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val args = FunctionCallArgs(emptyList(), emptyList())
|
||||||
|
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = args), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw AssemblyError("invalid node type")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun checkIfConditionCanUseBIT(condition: PtBinaryExpression): Triple<Boolean, PtIdentifier, Int>? {
|
||||||
|
// test for occurrence of: x & 64 != 0 (or 128) , this can be performed with a BIT instruction
|
||||||
|
if(condition.operator == "==" || condition.operator == "!=") {
|
||||||
|
if (condition.right.asConstInteger() == 0) {
|
||||||
|
val and = condition.left as? PtBinaryExpression
|
||||||
|
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
|
||||||
|
val bitmask = and.right.asConstInteger()
|
||||||
|
if(bitmask==128 || bitmask==64) {
|
||||||
|
val variable = and.left as? PtIdentifier
|
||||||
|
if (variable != null && variable.type.isByte) {
|
||||||
|
return Triple(condition.operator=="!=", variable, bitmask)
|
||||||
|
}
|
||||||
|
val typecast = and.left as? PtTypeCast
|
||||||
|
if (typecast != null && typecast.type.isUnsignedByte) {
|
||||||
|
val castedVariable = typecast.value as? PtIdentifier
|
||||||
|
if(castedVariable!=null && castedVariable.type.isByte)
|
||||||
|
return Triple(condition.operator=="!=", castedVariable, bitmask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
||||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||||
when(callTarget.scopedName) {
|
when(callTarget.scopedName) {
|
||||||
@ -743,13 +828,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
"sys.pop" -> {
|
"sys.pop" -> {
|
||||||
// pop byte
|
// pop byte
|
||||||
val popReg = codeGen.registers.nextFree()
|
val popReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=popReg), null)
|
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=popReg), null)
|
||||||
return ExpressionCodeResult(chunk, IRDataType.BYTE, popReg, -1)
|
return ExpressionCodeResult(chunk, IRDataType.BYTE, popReg, -1)
|
||||||
}
|
}
|
||||||
"sys.popw" -> {
|
"sys.popw" -> {
|
||||||
// pop word
|
// pop word
|
||||||
val popReg = codeGen.registers.nextFree()
|
val popReg = codeGen.registers.next(IRDataType.WORD)
|
||||||
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.WORD, reg1=popReg), null)
|
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.WORD, reg1=popReg), null)
|
||||||
return ExpressionCodeResult(chunk, IRDataType.WORD, popReg, -1)
|
return ExpressionCodeResult(chunk, IRDataType.WORD, popReg, -1)
|
||||||
}
|
}
|
||||||
@ -762,7 +847,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
"floats.pop" -> {
|
"floats.pop" -> {
|
||||||
// pop float
|
// pop float
|
||||||
val popReg = codeGen.registers.nextFreeFloat()
|
val popReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.FLOAT, fpReg1 = popReg), null)
|
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.FLOAT, fpReg1 = popReg), null)
|
||||||
return ExpressionCodeResult(chunk, IRDataType.FLOAT, -1, resultFpReg = popReg)
|
return ExpressionCodeResult(chunk, IRDataType.FLOAT, -1, resultFpReg = popReg)
|
||||||
}
|
}
|
||||||
@ -778,7 +863,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
): ExpressionCodeResult {
|
): ExpressionCodeResult {
|
||||||
// return multiple values
|
// return multiple values
|
||||||
val returnRegisters = callTarget.returns.map {
|
val returnRegisters = callTarget.returns.map {
|
||||||
val regnum = if(it.type.isFloat) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree()
|
val regnum = codeGen.registers.next(irType(it.type))
|
||||||
FunctionCallArgs.RegSpec(irType(it.type), regnum, it.register)
|
FunctionCallArgs.RegSpec(irType(it.type), regnum, it.register)
|
||||||
}
|
}
|
||||||
// create the call
|
// create the call
|
||||||
@ -809,23 +894,36 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
greaterEquals: Boolean
|
greaterEquals: Boolean
|
||||||
): ExpressionCodeResult {
|
): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val cmpResultReg = codeGen.registers.nextFree()
|
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||||
val zeroRegister = codeGen.registers.nextFree()
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultRegister, immediate = 0), null)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
|
val other = codeGen.createLabelName()
|
||||||
val ins = if (signed) {
|
val after = codeGen.createLabelName()
|
||||||
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
// TODO can this be done more efficiently? also see operatorLessThan
|
||||||
|
if(greaterEquals) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.BSTPOS, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
|
||||||
} else {
|
} else {
|
||||||
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.BSTNEG, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.BSTEQ, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 1)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 0), other)
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null)
|
result += IRCodeChunk(after, null)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
||||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||||
@ -834,13 +932,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val ins = if (signed) {
|
val branch = if (signed) {
|
||||||
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
if (greaterEquals) Opcode.BGESR else Opcode.BGTSR
|
||||||
} else {
|
} else {
|
||||||
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
if (greaterEquals) Opcode.BGER else Opcode.BGTR
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
|
val resultReg = compareRegisterAsBooleanResult(branch, leftTr.dt, leftTr.resultReg, rightTr.resultReg, result)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -852,23 +950,36 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
lessEquals: Boolean
|
lessEquals: Boolean
|
||||||
): ExpressionCodeResult {
|
): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val cmpResultRegister = codeGen.registers.nextFree()
|
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||||
val zeroRegister = codeGen.registers.nextFree()
|
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultRegister, immediate = 0), null)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
|
val other = codeGen.createLabelName()
|
||||||
val ins = if (signed) {
|
val after = codeGen.createLabelName()
|
||||||
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
// TODO can this be done more efficiently? also see operatorGreaterThan
|
||||||
|
if(lessEquals) {
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.BSTNEG, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.BSTEQ, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
|
||||||
} else {
|
} else {
|
||||||
if (lessEquals) Opcode.SLE else Opcode.SLT
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.BSTNEG, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null)
|
result += IRCodeChunk(after, null)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
||||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||||
@ -877,13 +988,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val ins = if (signed) {
|
val branch = if (signed) {
|
||||||
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
if (lessEquals) Opcode.BGESR else Opcode.BGTSR
|
||||||
} else {
|
} else {
|
||||||
if (lessEquals) Opcode.SLE else Opcode.SLT
|
if (lessEquals) Opcode.BGER else Opcode.BGTR
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultRegister, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
|
val resultReg = compareRegisterAsBooleanResult(branch, leftTr.dt, rightTr.resultReg, leftTr.resultReg, result)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,18 +1006,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
val resultRegister = codeGen.registers.nextFree()
|
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val valueReg = codeGen.registers.nextFree()
|
val valueReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
val label = codeGen.createLabelName()
|
val label = codeGen.createLabelName()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1)
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0)
|
||||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
|
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
|
||||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=valueReg, immediate = 0)
|
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=valueReg, immediate = 0)
|
||||||
it += if (notEquals)
|
it += if (notEquals)
|
||||||
IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
|
||||||
else
|
|
||||||
IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0)
|
else
|
||||||
|
IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(label, null)
|
result += IRCodeChunk(label, null)
|
||||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||||
@ -914,27 +1025,56 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
if(binExpr.left.type.isString || binExpr.right.type.isString) {
|
||||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||||
} else {
|
} else {
|
||||||
return if(binExpr.right.asConstValue()==0.0) {
|
val rightConst = binExpr.right.asConstValue()
|
||||||
val tr = translateExpression(binExpr.left)
|
return if(rightConst!=null) {
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
val opcode = if (notEquals) Opcode.SNZ else Opcode.SZ
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
addInstr(result, IRInstruction(Opcode.CMPI, leftTr.dt, reg1 = leftTr.resultReg, immediate = rightConst.toInt()), null)
|
||||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = tr.resultReg), null)
|
val resultReg = loadStatusAsBooleanResult(if(notEquals) Opcode.BSTNE else Opcode.BSTEQ, result)
|
||||||
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
|
addInstr(result, IRInstruction(Opcode.CMP, leftTr.dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg), null)
|
||||||
val resultReg = codeGen.registers.nextFree()
|
val resultReg = loadStatusAsBooleanResult(if(notEquals) Opcode.BSTNE else Opcode.BSTEQ, result)
|
||||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
|
|
||||||
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
|
||||||
|
// 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)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(branchForTrue, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
|
||||||
|
result += IRCodeChunk(after, null)
|
||||||
|
return resultReg
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
val other = codeGen.createLabelName()
|
||||||
|
val after = codeGen.createLabelName()
|
||||||
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(branchForTrue, dt, reg1=reg1, reg2=reg2, labelSymbol = other)
|
||||||
|
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = resultReg, immediate = 0)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultReg, immediate = 1), other)
|
||||||
|
result += IRCodeChunk(after, null)
|
||||||
|
return resultReg
|
||||||
|
}
|
||||||
|
|
||||||
private fun operatorShiftRight(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
|
private fun operatorShiftRight(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
return if(codeGen.isOne(binExpr.right)) {
|
return if(codeGen.isOne(binExpr.right)) {
|
||||||
@ -947,6 +1087,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
|
require(rightTr.dt== IRDataType.BYTE) { "can only shift by 0-255" }
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
|
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
|
||||||
addInstr(result, IRInstruction(opc, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
addInstr(result, IRInstruction(opc, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||||
@ -965,6 +1106,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
|
require(rightTr.dt== IRDataType.BYTE) { "can only shift by 0-255" }
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
addInstr(result, IRInstruction(Opcode.LSLN, vmDt, reg1=leftTr.resultReg, rightTr.resultReg), null)
|
addInstr(result, IRInstruction(Opcode.LSLN, vmDt, reg1=leftTr.resultReg, rightTr.resultReg), null)
|
||||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||||
@ -1079,7 +1221,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 result = mutableListOf<IRCodeChunkBase>()
|
||||||
val constFactorRight = binExpr.right as? PtNumber
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
@ -1094,7 +1237,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
addInstr(result, if(signed)
|
addInstr(result, if(dt.isSigned)
|
||||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||||
@ -1106,13 +1249,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(binExpr.left)
|
val tr = translateExpression(binExpr.left)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorRight.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
if(binExpr.right is PtNumber) {
|
if(binExpr.right is PtNumber) {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
addInstr(result, if (signed)
|
addInstr(result, if (dt.isSigned)
|
||||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||||
@ -1123,7 +1266,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
addInstr(result, if (signed)
|
addInstr(result, if (dt.isSigned)
|
||||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||||
@ -1134,7 +1277,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 result = mutableListOf<IRCodeChunkBase>()
|
||||||
val constFactorLeft = binExpr.left as? PtNumber
|
val constFactorLeft = binExpr.left as? PtNumber
|
||||||
val constFactorRight = binExpr.right as? PtNumber
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
@ -1156,7 +1300,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
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)
|
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1164,20 +1308,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(binExpr.right)
|
val tr = translateExpression(binExpr.right)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorLeft.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||||
val tr = translateExpression(binExpr.left)
|
val tr = translateExpression(binExpr.left)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorRight.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
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)
|
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class IRCodeGen(
|
|||||||
verifyNameScoping(program, symbolTable)
|
verifyNameScoping(program, symbolTable)
|
||||||
changeGlobalVarInits(symbolTable)
|
changeGlobalVarInits(symbolTable)
|
||||||
|
|
||||||
val irSymbolTable = IRSymbolTable.fromAstSymboltable(symbolTable)
|
val irSymbolTable = convertStToIRSt(symbolTable)
|
||||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||||
|
|
||||||
// collect global variables initializers
|
// collect global variables initializers
|
||||||
@ -48,6 +48,7 @@ class IRCodeGen(
|
|||||||
irProg.linkChunks()
|
irProg.linkChunks()
|
||||||
irProg.convertAsmChunks()
|
irProg.convertAsmChunks()
|
||||||
|
|
||||||
|
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||||
val optimizer = IRPeepholeOptimizer(irProg)
|
val optimizer = IRPeepholeOptimizer(irProg)
|
||||||
optimizer.optimize(options.optimize, errors)
|
optimizer.optimize(options.optimize, errors)
|
||||||
irProg.validate()
|
irProg.validate()
|
||||||
@ -64,15 +65,17 @@ class IRCodeGen(
|
|||||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||||
val block = variable.parent.astNode as PtBlock
|
val block = variable.parent.astNode as PtBlock
|
||||||
val initialization = (block.children.firstOrNull {
|
val initialization = (block.children.firstOrNull {
|
||||||
it is PtAssignment && it.target.identifier?.name==variable.scopedName
|
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
|
||||||
} as PtAssignment?)
|
} as PtAssignment?)
|
||||||
val initValue = initialization?.value
|
val initValue = initialization?.value
|
||||||
when(initValue){
|
when(initValue){
|
||||||
is PtBool -> {
|
is PtBool -> {
|
||||||
|
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
|
||||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||||
initsToRemove += block to initialization
|
initsToRemove += block to initialization
|
||||||
}
|
}
|
||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
|
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||||
variable.setOnetimeInitNumeric(initValue.number)
|
variable.setOnetimeInitNumeric(initValue.number)
|
||||||
initsToRemove += block to initialization
|
initsToRemove += block to initialization
|
||||||
}
|
}
|
||||||
@ -194,10 +197,7 @@ class IRCodeGen(
|
|||||||
old.fpReg1,
|
old.fpReg1,
|
||||||
old.fpReg2,
|
old.fpReg2,
|
||||||
immediate = immediateValue,
|
immediate = immediateValue,
|
||||||
null,
|
address = addressValue
|
||||||
address = addressValue,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +231,11 @@ class IRCodeGen(
|
|||||||
chunk += IRInstruction(Opcode.BREAKPOINT)
|
chunk += IRInstruction(Opcode.BREAKPOINT)
|
||||||
listOf(chunk)
|
listOf(chunk)
|
||||||
}
|
}
|
||||||
is PtAlign -> TODO("ir support for inline %align")
|
is PtAlign -> {
|
||||||
|
val chunk = IRCodeChunk(null, null)
|
||||||
|
chunk += IRInstruction(Opcode.ALIGN, immediate = node.align.toInt())
|
||||||
|
listOf(chunk)
|
||||||
|
}
|
||||||
is PtConditionalBranch -> translate(node)
|
is PtConditionalBranch -> translate(node)
|
||||||
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
|
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
|
||||||
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
|
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
|
||||||
@ -252,8 +256,8 @@ class IRCodeGen(
|
|||||||
is PtBool,
|
is PtBool,
|
||||||
is PtArray,
|
is PtArray,
|
||||||
is PtBlock,
|
is PtBlock,
|
||||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
|
||||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||||
else -> TODO("missing codegen for $node")
|
else -> TODO("missing codegen for $node")
|
||||||
}
|
}
|
||||||
@ -280,31 +284,23 @@ class IRCodeGen(
|
|||||||
val address = goto.target.asConstInteger()
|
val address = goto.target.asConstInteger()
|
||||||
val label = (goto.target as? PtIdentifier)?.name
|
val label = (goto.target as? PtIdentifier)?.name
|
||||||
if(address!=null) {
|
if(address!=null) {
|
||||||
val branchIns = when(branch.condition) {
|
val branchIns = IRBranchInstr(branch.condition, address=address)
|
||||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
|
|
||||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, address = address)
|
|
||||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, address = address)
|
|
||||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, address = address)
|
|
||||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, address = address)
|
|
||||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, address = address)
|
|
||||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, address = address)
|
|
||||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
|
|
||||||
}
|
|
||||||
addInstr(result, branchIns, null)
|
addInstr(result, branchIns, null)
|
||||||
} else if(label!=null && !isIndirectJump(goto)) {
|
} else if(label!=null && !isIndirectJump(goto)) {
|
||||||
val branchIns = when(branch.condition) {
|
val branchIns = IRBranchInstr(branch.condition, label = label)
|
||||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
|
||||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
|
||||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
|
||||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
|
||||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
|
||||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
|
||||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
|
||||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
|
||||||
}
|
|
||||||
addInstr(result, branchIns, null)
|
addInstr(result, branchIns, null)
|
||||||
} else {
|
} else {
|
||||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
val skipJumpLabel = createLabelName()
|
||||||
|
// note that the branch opcode used is the opposite as the branch condition, because it needs to skip the indirect jump
|
||||||
|
val branchIns = IRInvertedBranchInstr(branch.condition, label = skipJumpLabel)
|
||||||
|
// evaluate jump address expression into a register and jump indirectly to it
|
||||||
|
addInstr(result, branchIns, null)
|
||||||
|
val tr = expressionEval.translateExpression(goto.target)
|
||||||
|
result += tr.chunks
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(skipJumpLabel, null)
|
||||||
}
|
}
|
||||||
if(branch.falseScope.children.isNotEmpty())
|
if(branch.falseScope.children.isNotEmpty())
|
||||||
result += translateNode(branch.falseScope)
|
result += translateNode(branch.falseScope)
|
||||||
@ -313,16 +309,7 @@ class IRCodeGen(
|
|||||||
|
|
||||||
val elseLabel = createLabelName()
|
val elseLabel = createLabelName()
|
||||||
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||||
val branchIns = when(branch.condition) {
|
val branchIns = IRInvertedBranchInstr(branch.condition, label = elseLabel)
|
||||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, labelSymbol = elseLabel)
|
|
||||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, labelSymbol = elseLabel)
|
|
||||||
}
|
|
||||||
addInstr(result, branchIns, null)
|
addInstr(result, branchIns, null)
|
||||||
result += translateNode(branch.trueScope)
|
result += translateNode(branch.trueScope)
|
||||||
if(branch.falseScope.children.isNotEmpty()) {
|
if(branch.falseScope.children.isNotEmpty()) {
|
||||||
@ -337,6 +324,60 @@ class IRCodeGen(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||||
|
if(label!=null)
|
||||||
|
return when(condition) {
|
||||||
|
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||||
|
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
||||||
|
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
||||||
|
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
||||||
|
}
|
||||||
|
else if(address!=null) {
|
||||||
|
return when(condition) {
|
||||||
|
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
|
||||||
|
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, address = address)
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, address = address)
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||||
|
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, address = address)
|
||||||
|
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw AssemblyError("need label or address for branch")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IRInvertedBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||||
|
if(label!=null)
|
||||||
|
return when(condition) {
|
||||||
|
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||||
|
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
||||||
|
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
||||||
|
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
||||||
|
}
|
||||||
|
else if(address!=null) {
|
||||||
|
return when(condition) {
|
||||||
|
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, address = address)
|
||||||
|
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, address = address)
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, address = address)
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||||
|
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, address = address)
|
||||||
|
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, address = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw AssemblyError("need label or address for branch")
|
||||||
|
}
|
||||||
|
|
||||||
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
||||||
if(chunks.isEmpty()) {
|
if(chunks.isEmpty()) {
|
||||||
return listOf(
|
return listOf(
|
||||||
@ -379,35 +420,44 @@ class IRCodeGen(
|
|||||||
whenStmt.choices.children.forEach {
|
whenStmt.choices.children.forEach {
|
||||||
val choice = it as PtWhenChoice
|
val choice = it as PtWhenChoice
|
||||||
if(choice.isElse) {
|
if(choice.isElse) {
|
||||||
|
require(choice.parent.children.last() === choice)
|
||||||
result += translateNode(choice.statements)
|
result += translateNode(choice.statements)
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
// is always the last node so can fall through
|
||||||
} else {
|
} else {
|
||||||
if(choice.statements.children.isEmpty()) {
|
if(choice.statements.children.isEmpty()) {
|
||||||
// no statements for this choice value, jump to the end immediately
|
// no statements for this choice value, jump to the end immediately
|
||||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also { chunk ->
|
||||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val choiceLabel = createLabelName()
|
val choiceLabel = createLabelName()
|
||||||
choices.add(choiceLabel to choice)
|
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
val branchLabel: String
|
||||||
result += IRCodeChunk(null, null).also {
|
if(onlyJumpLabel==null) {
|
||||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
choices.add(choiceLabel to choice)
|
||||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
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) ->
|
choices.forEach { (label, choice) ->
|
||||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||||
val lastStatement = choice.statements.children.last()
|
if(!choice.isOnlyGotoOrReturn())
|
||||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,10 +478,11 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
require(forLoop.variable.name == loopvar.scopedName)
|
require(forLoop.variable.name == loopvar.scopedName)
|
||||||
|
val elementDt = irType(iterable.type.elementType())
|
||||||
val iterableLength = symbolTable.getLength(iterable.name)
|
val iterableLength = symbolTable.getLength(iterable.name)
|
||||||
val loopvarSymbol = forLoop.variable.name
|
val loopvarSymbol = forLoop.variable.name
|
||||||
val indexReg = registers.nextFree()
|
val indexReg = registers.next(IRDataType.BYTE)
|
||||||
val tmpReg = registers.nextFree()
|
val tmpReg = registers.next(elementDt)
|
||||||
val loopLabel = createLabelName()
|
val loopLabel = createLabelName()
|
||||||
val endLabel = createLabelName()
|
val endLabel = createLabelName()
|
||||||
when {
|
when {
|
||||||
@ -439,9 +490,9 @@ class IRCodeGen(
|
|||||||
// iterate over a zero-terminated string
|
// iterate over a zero-terminated string
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||||
result += IRCodeChunk(loopLabel, null).also {
|
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.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)
|
result += translateNode(forLoop.statements)
|
||||||
val jumpChunk = IRCodeChunk(null, null)
|
val jumpChunk = IRCodeChunk(null, null)
|
||||||
@ -452,18 +503,17 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
iterable.type.isSplitWordArray -> {
|
iterable.type.isSplitWordArray -> {
|
||||||
// iterate over lsb/msb split word array
|
// iterate over lsb/msb split word array
|
||||||
val elementDt = iterable.type.elementType()
|
if(elementDt!=IRDataType.WORD)
|
||||||
if(!elementDt.isWord)
|
|
||||||
throw AssemblyError("weird dt")
|
throw AssemblyError("weird dt")
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||||
result += IRCodeChunk(loopLabel, null).also {
|
result += IRCodeChunk(loopLabel, null).also {
|
||||||
val tmpRegLsb = registers.nextFree()
|
val tmpRegLsb = registers.next(IRDataType.BYTE)
|
||||||
val tmpRegMsb = registers.nextFree()
|
val tmpRegMsb = registers.next(IRDataType.BYTE)
|
||||||
val concatReg = registers.nextFree()
|
val concatReg = registers.next(IRDataType.WORD)
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
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.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.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 += translateNode(forLoop.statements)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
@ -595,7 +645,6 @@ class IRCodeGen(
|
|||||||
val loopLabel = createLabelName()
|
val loopLabel = createLabelName()
|
||||||
require(forLoop.variable.name == loopvar.scopedName)
|
require(forLoop.variable.name == loopvar.scopedName)
|
||||||
val loopvarSymbol = forLoop.variable.name
|
val loopvarSymbol = forLoop.variable.name
|
||||||
val indexReg = registers.nextFree()
|
|
||||||
val loopvarDt = when(loopvar) {
|
val loopvarDt = when(loopvar) {
|
||||||
is StMemVar -> loopvar.dt
|
is StMemVar -> loopvar.dt
|
||||||
is StStaticVariable -> loopvar.dt
|
is StStaticVariable -> loopvar.dt
|
||||||
@ -611,6 +660,7 @@ class IRCodeGen(
|
|||||||
val rangeEndExclusiveWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndExclusiveUntyped and 255 else rangeEndExclusiveUntyped and 65535
|
val rangeEndExclusiveWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndExclusiveUntyped and 255 else rangeEndExclusiveUntyped and 65535
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val chunk = IRCodeChunk(null, null)
|
val chunk = IRCodeChunk(null, null)
|
||||||
|
val indexReg = registers.next(loopvarDtIr)
|
||||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = iterable.first)
|
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = iterable.first)
|
||||||
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
|
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||||
result += chunk
|
result += chunk
|
||||||
@ -703,7 +753,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val valueReg = registers.nextFree()
|
val valueReg = registers.next(dt)
|
||||||
if(value>0) {
|
if(value>0) {
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, immediate = value)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, immediate = value)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
@ -730,7 +780,7 @@ class IRCodeGen(
|
|||||||
code += if(factor==0.0) {
|
code += if(factor==0.0) {
|
||||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||||
} else {
|
} else {
|
||||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@ -745,41 +795,43 @@ class IRCodeGen(
|
|||||||
else
|
else
|
||||||
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, labelSymbol = symbol)
|
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, labelSymbol = symbol)
|
||||||
} else {
|
} else {
|
||||||
val factorReg = registers.nextFreeFloat()
|
val factorReg = registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
return code
|
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)
|
val code = IRCodeChunk(null, null)
|
||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||||
if(pow2==1) {
|
if(pow2==1) {
|
||||||
// just shift 1 bit
|
// just shift 1 bit
|
||||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||||
}
|
}
|
||||||
else if(pow2>=1) {
|
else if(pow2>=1) {
|
||||||
// just shift multiple bits
|
// just shift multiple bits
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(IRDataType.BYTE)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
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 {
|
} else {
|
||||||
code += if (factor == 0) {
|
code += if (factor == 0) {
|
||||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||||
} else {
|
} 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
|
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)
|
val code = IRCodeChunk(null, null)
|
||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
@ -793,7 +845,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
else if(pow2>=1) {
|
else if(pow2>=1) {
|
||||||
// just shift multiple bits
|
// just shift multiple bits
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, address = knownAddress)
|
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, address = knownAddress)
|
||||||
@ -807,12 +859,13 @@ class IRCodeGen(
|
|||||||
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val factorReg = registers.nextFree()
|
val factorReg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||||
|
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -835,14 +888,14 @@ class IRCodeGen(
|
|||||||
if(factor==1.0)
|
if(factor==1.0)
|
||||||
return code
|
return code
|
||||||
if(factor==0.0) {
|
if(factor==0.0) {
|
||||||
val maxvalueReg = registers.nextFreeFloat()
|
val maxvalueReg = registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Double.MAX_VALUE)
|
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Double.MAX_VALUE)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress)
|
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
||||||
} else {
|
} else {
|
||||||
val factorReg = registers.nextFreeFloat()
|
val factorReg = registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||||
@ -864,8 +917,8 @@ class IRCodeGen(
|
|||||||
code += IRInstruction(Opcode.ASR, dt, reg1=reg)
|
code += IRInstruction(Opcode.ASR, dt, reg1=reg)
|
||||||
} else {
|
} else {
|
||||||
// just shift multiple bits (signed)
|
// just shift multiple bits (signed)
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(IRDataType.BYTE)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||||
code += IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
code += IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -874,8 +927,8 @@ class IRCodeGen(
|
|||||||
code += IRInstruction(Opcode.LSR, dt, reg1=reg)
|
code += IRInstruction(Opcode.LSR, dt, reg1=reg)
|
||||||
} else {
|
} else {
|
||||||
// just shift multiple bits (unsigned)
|
// just shift multiple bits (unsigned)
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(IRDataType.BYTE)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
|
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = pow2reg, immediate = pow2)
|
||||||
code += IRInstruction(Opcode.LSRN, dt, reg1 = reg, reg2 = pow2reg)
|
code += IRInstruction(Opcode.LSRN, dt, reg1 = reg, reg2 = pow2reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +963,7 @@ class IRCodeGen(
|
|||||||
IRInstruction(Opcode.ASRM, dt, labelSymbol = symbol)
|
IRInstruction(Opcode.ASRM, dt, labelSymbol = symbol)
|
||||||
} else {
|
} else {
|
||||||
// just shift multiple bits (signed)
|
// just shift multiple bits (signed)
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
|
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
|
||||||
code += if (knownAddress != null)
|
code += if (knownAddress != null)
|
||||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, address = knownAddress)
|
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||||
@ -927,7 +980,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// just shift multiple bits (unsigned)
|
// just shift multiple bits (unsigned)
|
||||||
val pow2reg = registers.nextFree()
|
val pow2reg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, address = knownAddress)
|
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||||
@ -941,7 +994,7 @@ class IRCodeGen(
|
|||||||
{
|
{
|
||||||
// regular div
|
// regular div
|
||||||
if (factor == 0) {
|
if (factor == 0) {
|
||||||
val reg = registers.nextFree()
|
val reg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0xffff)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0xffff)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, address = knownAddress)
|
IRInstruction(Opcode.STOREM, dt, reg1=reg, address = knownAddress)
|
||||||
@ -949,7 +1002,7 @@ class IRCodeGen(
|
|||||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val factorReg = registers.nextFree()
|
val factorReg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||||
code += if(signed) {
|
code += if(signed) {
|
||||||
if(knownAddress!=null)
|
if(knownAddress!=null)
|
||||||
@ -986,6 +1039,7 @@ class IRCodeGen(
|
|||||||
ifWithOnlyNormalJump_IntegerCond(ifElse, goto)
|
ifWithOnlyNormalJump_IntegerCond(ifElse, goto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// floating-point condition only from here!
|
||||||
// we assume only a binary expression can contain a floating point.
|
// we assume only a binary expression can contain a floating point.
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val leftTr = expressionEval.translateExpression(condition.left)
|
val leftTr = expressionEval.translateExpression(condition.left)
|
||||||
@ -994,7 +1048,7 @@ class IRCodeGen(
|
|||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
var afterIfLabel = ""
|
var afterIfLabel = ""
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val compResultReg = registers.nextFree()
|
val compResultReg = registers.next(IRDataType.BYTE)
|
||||||
it += IRInstruction(
|
it += IRInstruction(
|
||||||
Opcode.FCOMP,
|
Opcode.FCOMP,
|
||||||
IRDataType.FLOAT,
|
IRDataType.FLOAT,
|
||||||
@ -1028,7 +1082,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
// evaluate jump address expression into a register and jump indirectly to it
|
// evaluate jump address expression into a register and jump indirectly to it
|
||||||
val tr = expressionEval.translateExpression(goto.target)
|
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 += i
|
||||||
}
|
}
|
||||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||||
@ -1056,7 +1110,7 @@ class IRCodeGen(
|
|||||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
|
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
|
||||||
else
|
else
|
||||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1075,7 +1129,7 @@ class IRCodeGen(
|
|||||||
if(identifier!=null && !isIndirectJump(goto))
|
if(identifier!=null && !isIndirectJump(goto))
|
||||||
IRInstruction(branchOpcode, labelSymbol = identifier.name)
|
IRInstruction(branchOpcode, labelSymbol = identifier.name)
|
||||||
else
|
else
|
||||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
TODO("JUMP to expression address ${goto.target}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1124,7 +1178,6 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
val rightTr = expressionEval.translateExpression(condition.right)
|
val rightTr = expressionEval.translateExpression(condition.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
val firstReg: Int
|
val firstReg: Int
|
||||||
@ -1254,7 +1307,7 @@ class IRCodeGen(
|
|||||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
|
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
|
||||||
else
|
else
|
||||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1313,7 +1366,7 @@ class IRCodeGen(
|
|||||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
|
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
|
||||||
else
|
else
|
||||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1357,7 +1410,7 @@ class IRCodeGen(
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = expressionEval.translateExpression(condition.right)
|
val rightTr = expressionEval.translateExpression(condition.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||||
val compResultReg = registers.nextFree()
|
val compResultReg = registers.next(IRDataType.BYTE)
|
||||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1 = compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1 = compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||||
val elseBranch: Opcode
|
val elseBranch: Opcode
|
||||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||||
@ -1412,10 +1465,6 @@ class IRCodeGen(
|
|||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
|
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
|
||||||
|
|
||||||
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
|
|
||||||
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
|
|
||||||
|
|
||||||
val tr = expressionEval.translateExpression(condition)
|
val tr = expressionEval.translateExpression(condition)
|
||||||
if(addCmpiZero)
|
if(addCmpiZero)
|
||||||
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
|
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
|
||||||
@ -1440,6 +1489,41 @@ class IRCodeGen(
|
|||||||
if(condition.operator in LogicalOperators)
|
if(condition.operator in LogicalOperators)
|
||||||
return translateSimple(condition, Opcode.BSTEQ, false)
|
return translateSimple(condition, Opcode.BSTEQ, false)
|
||||||
|
|
||||||
|
val useBIT = expressionEval.checkIfConditionCanUseBIT(condition)
|
||||||
|
if(useBIT!=null) {
|
||||||
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
|
val (testBitSet, variable, bitmask) = useBIT
|
||||||
|
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = variable.name), null)
|
||||||
|
val bitBranchOpcode = when(testBitSet) {
|
||||||
|
true -> when(bitmask) {
|
||||||
|
64 -> Opcode.BSTVC
|
||||||
|
128 -> Opcode.BSTPOS
|
||||||
|
else -> throw AssemblyError("need bit 6 or 7")
|
||||||
|
}
|
||||||
|
false -> when(bitmask) {
|
||||||
|
64 -> Opcode.BSTVS
|
||||||
|
128 -> Opcode.BSTNEG
|
||||||
|
else -> throw AssemblyError("need bit 6 or 7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ifElse.hasElse()) {
|
||||||
|
val elseLabel = createLabelName()
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||||
|
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
} else {
|
||||||
|
val afterIfLabel = createLabelName()
|
||||||
|
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
|
||||||
|
result += translateNode(ifElse.ifScope)
|
||||||
|
result += IRCodeChunk(afterIfLabel, null)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val signed = condition.left.type.isSigned
|
val signed = condition.left.type.isSigned
|
||||||
val elseBranchFirstReg: Int
|
val elseBranchFirstReg: Int
|
||||||
val elseBranchSecondReg: Int
|
val elseBranchSecondReg: Int
|
||||||
@ -1618,7 +1702,7 @@ class IRCodeGen(
|
|||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(constRepeats==65536) {
|
if(constRepeats==65536) {
|
||||||
// make use of the word wrap around to count to 65536
|
// make use of the word wrap around to count to 65536
|
||||||
val resultRegister = registers.nextFree()
|
val resultRegister = registers.next(IRDataType.WORD)
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, immediate = 0), null)
|
||||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
@ -1686,10 +1770,46 @@ class IRCodeGen(
|
|||||||
|
|
||||||
private fun translate(ret: PtReturn): IRCodeChunks {
|
private fun translate(ret: PtReturn): IRCodeChunks {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val value = ret.value
|
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)
|
||||||
|
// 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)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = ret.children.singleOrNull()
|
||||||
if(value==null) {
|
if(value==null) {
|
||||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||||
} else {
|
} else {
|
||||||
|
value as PtExpression
|
||||||
if(value.type.isFloat) {
|
if(value.type.isFloat) {
|
||||||
if(value is PtNumber) {
|
if(value is PtNumber) {
|
||||||
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
|
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
|
||||||
@ -1728,7 +1848,7 @@ class IRCodeGen(
|
|||||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||||
is PtAlign -> TODO("ir support for inline %align")
|
is PtAlign -> TODO("ir support for inline %align")
|
||||||
is PtSub -> {
|
is PtSub -> {
|
||||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
|
||||||
for (subchild in child.children) {
|
for (subchild in child.children) {
|
||||||
translateNode(subchild).forEach { sub += it }
|
translateNode(subchild).forEach { sub += it }
|
||||||
}
|
}
|
||||||
@ -1769,6 +1889,14 @@ class IRCodeGen(
|
|||||||
is PtLabel -> {
|
is PtLabel -> {
|
||||||
irBlock += IRCodeChunk(child.name, null)
|
irBlock += IRCodeChunk(child.name, null)
|
||||||
}
|
}
|
||||||
|
is PtJmpTable -> {
|
||||||
|
irBlock += IRCodeChunk(null, null).also {
|
||||||
|
for(addr in child.children) {
|
||||||
|
addr as PtIdentifier
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = addr.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> TODO("weird block child node $child")
|
else -> TODO("weird block child node $child")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1798,7 +1926,7 @@ class IRCodeGen(
|
|||||||
private var labelSequenceNumber = 0
|
private var labelSequenceNumber = 0
|
||||||
internal fun createLabelName(): String {
|
internal fun createLabelName(): String {
|
||||||
labelSequenceNumber++
|
labelSequenceNumber++
|
||||||
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
return "${GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||||
@ -1818,4 +1946,30 @@ class IRCodeGen(
|
|||||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec))
|
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||||
|
|
||||||
|
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)
|
||||||
|
RegisterOrPair.X -> chunk += IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=resultReg)
|
||||||
|
RegisterOrPair.Y -> chunk += IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=resultReg)
|
||||||
|
RegisterOrPair.AX -> chunk += IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=resultReg)
|
||||||
|
RegisterOrPair.AY -> chunk += IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=resultReg)
|
||||||
|
RegisterOrPair.XY -> chunk += IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=resultReg)
|
||||||
|
RegisterOrPair.FAC1 -> chunk += IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||||
|
RegisterOrPair.FAC2 -> chunk += IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||||
|
in Cx16VirtualRegisters -> {
|
||||||
|
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
|
||||||
|
}
|
||||||
|
null -> when(registerOrFlag.statusflag) {
|
||||||
|
// TODO: do the statusflag argument as last
|
||||||
|
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||||
|
else -> throw AssemblyError("unsupported statusflag as param")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("unsupported register arg")
|
||||||
|
}
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeOnlyJoinChunks() {
|
private fun optimizeOnlyJoinChunks() {
|
||||||
|
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||||
irprog.foreachSub { sub ->
|
irprog.foreachSub { sub ->
|
||||||
joinChunks(sub)
|
joinChunks(sub)
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
/*
|
/*
|
||||||
Empty Code chunk with label ->
|
Empty Code chunk with label ->
|
||||||
If next chunk has no label -> move label to next chunk, remove original
|
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.
|
If is last chunk -> keep chunk in place because of the label.
|
||||||
Empty Code chunk without label ->
|
Empty Code chunk without label ->
|
||||||
should not have been generated! ERROR.
|
should not have been generated! ERROR.
|
||||||
@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
if(index>0) {
|
if(index>0) {
|
||||||
if (chunk.label == nextchunk.label)
|
if (chunk.label == nextchunk.label)
|
||||||
removeChunks += index
|
removeChunks += index
|
||||||
else {
|
|
||||||
removeChunks += index
|
|
||||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
|
||||||
replaceLabels.entries.forEach { (key, value) ->
|
|
||||||
if (value == chunk.label)
|
|
||||||
replaceLabels[key] = nextchunk.label!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,6 +192,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
||||||
else
|
else
|
||||||
candidate
|
candidate
|
||||||
|
} else {
|
||||||
|
chunks += candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IRInlineBinaryChunk -> {
|
is IRInlineBinaryChunk -> {
|
||||||
@ -210,6 +205,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
||||||
else
|
else
|
||||||
candidate
|
candidate
|
||||||
|
} else {
|
||||||
|
chunks += candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,39 +348,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a SNZ etc. whose target register is not used can be removed altogether
|
|
||||||
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
|
|
||||||
val usages = regUsages(ins.reg1!!)
|
|
||||||
if(usages.toList().sumOf { it.second } <= 1) {
|
|
||||||
chunk.instructions.removeAt(idx)
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
|
|
||||||
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
|
|
||||||
irprog.foreachSub { sub ->
|
|
||||||
sub.chunks.forEach { chunk ->
|
|
||||||
val used = chunk.usedRegisters()
|
|
||||||
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
|
|
||||||
if(numUsages>0) {
|
|
||||||
chunks[chunk] = numUsages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
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
|
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||||
var changed = false
|
var changed = false
|
||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
when (ins.opcode) {
|
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) {
|
if (ins.immediate == 1) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
@ -403,15 +377,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.AND -> {
|
Opcode.AND -> {
|
||||||
if (ins.immediate == 0) {
|
when (ins.immediate) {
|
||||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
0 -> {
|
||||||
changed = true
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
changed = true
|
||||||
chunk.instructions.removeAt(idx)
|
}
|
||||||
changed = true
|
255 if ins.type == IRDataType.BYTE -> {
|
||||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
chunk.instructions.removeAt(idx)
|
||||||
chunk.instructions.removeAt(idx)
|
changed = true
|
||||||
changed = true
|
}
|
||||||
|
65535 if ins.type == IRDataType.WORD -> {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.OR -> {
|
Opcode.OR -> {
|
||||||
|
@ -173,7 +173,7 @@ class IRUnusedCodeRemover(
|
|||||||
if(chunk!=null)
|
if(chunk!=null)
|
||||||
new+=chunk
|
new+=chunk
|
||||||
else
|
else
|
||||||
allLabeledChunks[label]?.let { new += it }
|
allLabeledChunks[label]?.let { c -> new += c }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
new += instr.branchTarget!!
|
new += instr.branchTarget!!
|
||||||
@ -226,7 +226,7 @@ class IRUnusedCodeRemover(
|
|||||||
chunk.instructions.forEach {
|
chunk.instructions.forEach {
|
||||||
if(it.labelSymbol!=null) {
|
if(it.labelSymbol!=null) {
|
||||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,31 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.intermediate.IRDataType
|
||||||
|
|
||||||
internal class RegisterPool {
|
internal class RegisterPool {
|
||||||
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
|
// everything from 99000 onwards is reserved for special purposes:
|
||||||
private var firstFree: Int=3
|
// 99000 - 99099 : WORD registers for syscall arguments and response value(s)
|
||||||
private var firstFreeFloat: Int=3
|
// 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
|
||||||
|
|
||||||
fun peekNext() = firstFree
|
private var nextRegister: Int=1
|
||||||
fun peekNextFloat() = firstFreeFloat
|
private val registerTypes: MutableMap<Int, IRDataType> = mutableMapOf()
|
||||||
|
|
||||||
fun nextFree(): Int {
|
fun getTypes(): Map<Int, IRDataType> = registerTypes
|
||||||
val result = firstFree
|
|
||||||
firstFree++
|
init {
|
||||||
return result
|
for(i in 99000..99099)
|
||||||
|
registerTypes[i] = IRDataType.WORD
|
||||||
|
for(i in 99100..99199)
|
||||||
|
registerTypes[i] = IRDataType.BYTE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextFreeFloat(): Int {
|
fun next(type: IRDataType): Int {
|
||||||
val result = firstFreeFloat
|
if(nextRegister>=99000)
|
||||||
firstFreeFloat++
|
throw AssemblyError("register pool depleted")
|
||||||
|
val result = nextRegister
|
||||||
|
nextRegister++
|
||||||
|
registerTypes[result] = type
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
133
codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt
Normal file
133
codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.*
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
|
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||||
|
val st = IRSymbolTable()
|
||||||
|
if (sourceSt != null) {
|
||||||
|
sourceSt.flat.forEach {
|
||||||
|
when(it.value.type) {
|
||||||
|
StNodeType.STATICVAR -> st.add(convert(it.value as StStaticVariable))
|
||||||
|
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))
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
st.validate()
|
||||||
|
|
||||||
|
st.allVariables().forEach { variable ->
|
||||||
|
variable.onetimeInitializationArrayValue?.let {
|
||||||
|
it.forEach { arrayElt ->
|
||||||
|
val addrOfSymbol = arrayElt.addressOfSymbol
|
||||||
|
if (addrOfSymbol != null) {
|
||||||
|
require(addrOfSymbol.contains('.')) {
|
||||||
|
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if('.' in variable.name) {
|
||||||
|
return IRStStaticVariable(variable.name,
|
||||||
|
variable.dt,
|
||||||
|
variable.initializationNumericValue,
|
||||||
|
variable.initializationStringValue,
|
||||||
|
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||||
|
variable.length,
|
||||||
|
variable.zpwish,
|
||||||
|
variable.align,
|
||||||
|
variable.dirty)
|
||||||
|
} else {
|
||||||
|
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||||
|
if(array==null)
|
||||||
|
return null
|
||||||
|
val newArray = mutableListOf<IRStArrayElement>()
|
||||||
|
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))
|
||||||
|
} else {
|
||||||
|
newArray.add(convertArrayElt(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newArray
|
||||||
|
}
|
||||||
|
val scopedName = variable.scopedName
|
||||||
|
return IRStStaticVariable(scopedName,
|
||||||
|
variable.dt,
|
||||||
|
variable.initializationNumericValue,
|
||||||
|
variable.initializationStringValue,
|
||||||
|
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||||
|
variable.length,
|
||||||
|
variable.zpwish,
|
||||||
|
variable.align,
|
||||||
|
variable.dirty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun convert(variable: StMemVar): IRStMemVar {
|
||||||
|
if('.' in variable.name) {
|
||||||
|
return IRStMemVar(
|
||||||
|
variable.name,
|
||||||
|
variable.dt,
|
||||||
|
variable.address,
|
||||||
|
variable.length
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val scopedName = try {
|
||||||
|
variable.scopedName
|
||||||
|
} catch (_: UninitializedPropertyAccessException) {
|
||||||
|
variable.name
|
||||||
|
}
|
||||||
|
return IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun convert(constant: StConstant): IRStConstant {
|
||||||
|
val dt = DataType.forDt(constant.dt)
|
||||||
|
val scopedName = if('.' in constant.name) {
|
||||||
|
constant.name
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
constant.scopedName
|
||||||
|
} catch (_: UninitializedPropertyAccessException) {
|
||||||
|
constant.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IRStConstant(scopedName, dt, constant.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
@ -3,8 +3,8 @@ package prog8.codegen.vm
|
|||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyProgram
|
import prog8.code.IAssemblyProgram
|
||||||
import prog8.code.core.ICodeGeneratorBackend
|
import prog8.code.ICodeGeneratorBackend
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
@ -19,12 +19,18 @@ class VmCodeGen: ICodeGeneratorBackend {
|
|||||||
): IAssemblyProgram {
|
): IAssemblyProgram {
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
val irProgram = irCodeGen.generate()
|
val irProgram = irCodeGen.generate()
|
||||||
|
|
||||||
|
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
|
||||||
|
|
||||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
internal class VmAssemblyProgram(
|
||||||
|
override val name: String,
|
||||||
|
internal val irProgram: IRProgram
|
||||||
|
): IAssemblyProgram {
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
// the VM reads the IR file from disk.
|
// the VM reads the IR file from disk.
|
||||||
|
@ -66,6 +66,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||||
|
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||||
|
@ -9,7 +9,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||||
require(chunks.first().label=="main.start")
|
require(chunks.first().label=="main.start")
|
||||||
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
||||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
val sub = IRSubroutine("main.start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
chunks.forEach { sub += it }
|
chunks.forEach { sub += it }
|
||||||
block += sub
|
block += sub
|
||||||
val target = VMTarget()
|
val target = VMTarget()
|
||||||
@ -21,8 +21,9 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
CompilationOptions.AllZeropageAllowed,
|
CompilationOptions.AllZeropageAllowed,
|
||||||
floats = false,
|
floats = false,
|
||||||
noSysInit = true,
|
noSysInit = true,
|
||||||
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
||||||
@ -52,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
irProg.chunks().single().instructions.size shouldBe 1
|
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)
|
val c1 = IRCodeChunk("main.start", null)
|
||||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||||
val c2 = IRCodeChunk("label", null)
|
val c2 = IRCodeChunk("label", null)
|
||||||
@ -68,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize(true, ErrorReporterForTests())
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().size shouldBe 3
|
val chunks = irProg.chunks()
|
||||||
irProg.chunks()[0].label shouldBe "main.start"
|
chunks.size shouldBe 4
|
||||||
irProg.chunks()[1].label shouldBe "label2"
|
chunks[0].label shouldBe "main.start"
|
||||||
irProg.chunks()[2].label shouldBe "label3"
|
chunks[1].label shouldBe "label"
|
||||||
irProg.chunks()[0].isEmpty() shouldBe true
|
chunks[2].label shouldBe "label2"
|
||||||
irProg.chunks()[1].isEmpty() shouldBe false
|
chunks[3].label shouldBe "label3"
|
||||||
irProg.chunks()[2].isEmpty() shouldBe true
|
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 }
|
val instr = irProg.chunks().flatMap { it.instructions }
|
||||||
instr.size shouldBe 2
|
instr.size shouldBe 2
|
||||||
instr[0].opcode shouldBe Opcode.JUMP
|
instr[0].opcode shouldBe Opcode.JUMP
|
||||||
|
@ -23,8 +23,9 @@ class TestVmCodeGen: FunSpec({
|
|||||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||||
floats = true,
|
floats = true,
|
||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -45,13 +46,14 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"pi",
|
"pi",
|
||||||
DataType.forDt(BaseDataType.UBYTE),
|
DataType.UBYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
|
false,
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
@ -60,6 +62,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -69,23 +72,25 @@ class TestVmCodeGen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"xx",
|
"xx",
|
||||||
DataType.forDt(BaseDataType.WORD),
|
DataType.WORD,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
|
false,
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
|
|
||||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
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",
|
idx.add(PtIdentifier("main.start.particleX",
|
||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
Position.DUMMY))
|
Position.DUMMY))
|
||||||
@ -93,7 +98,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
}
|
}
|
||||||
it.add(targetIdx)
|
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",
|
value.add(PtIdentifier("main.start.particleDX",
|
||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
Position.DUMMY))
|
Position.DUMMY))
|
||||||
@ -104,15 +109,15 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(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)
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(numberAssignTarget)
|
||||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||||
@ -120,10 +125,10 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
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(cxregAssignTarget)
|
||||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
sub.add(cxregAssign)
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
@ -131,7 +136,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
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)
|
program.add(cx16block)
|
||||||
|
|
||||||
val options = getTestOptions()
|
val options = getTestOptions()
|
||||||
@ -160,43 +165,44 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -231,43 +237,44 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -298,27 +305,28 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
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().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
if1.add(PtNodeGroup())
|
if1.add(PtNodeGroup())
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
@ -353,43 +361,44 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"sb1",
|
"sb1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -424,43 +433,44 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"sb1",
|
"sb1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -491,27 +501,28 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"ub1",
|
"ub1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
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().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
if1.add(PtNodeGroup())
|
if1.add(PtNodeGroup())
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
@ -541,8 +552,8 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||||
block.add(extsub)
|
block.add(extsub)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
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)
|
sub.add(call)
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
program.add(block)
|
program.add(block)
|
||||||
@ -553,11 +564,8 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
irChunks.size shouldBe 1
|
irChunks.size shouldBe 1
|
||||||
irChunks[0].instructions.size shouldBe 2
|
irChunks[0].instructions.size shouldBe 1
|
||||||
val preparecallInstr = irChunks[0].instructions[0]
|
val callInstr = irChunks[0].instructions[0]
|
||||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
|
||||||
preparecallInstr.immediate shouldBe 0
|
|
||||||
val callInstr = irChunks[0].instructions[1]
|
|
||||||
callInstr.opcode shouldBe Opcode.CALL
|
callInstr.opcode shouldBe Opcode.CALL
|
||||||
callInstr.address shouldBe 0x5000
|
callInstr.address shouldBe 0x5000
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ dependencies {
|
|||||||
implementation(project(":codeCore"))
|
implementation(project(":codeCore"))
|
||||||
implementation(project(":compilerAst"))
|
implementation(project(":compilerAst"))
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,4 +23,4 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
// note: there are no unit tests in this module!
|
||||||
|
@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.FunctionCallExpression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.code.core.isInteger
|
|
||||||
import prog8.code.core.isIntegerOrBool
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||||
|
@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
|
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
|
||||||
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
||||||
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
||||||
}
|
}
|
||||||
@ -332,7 +332,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
|
|
||||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||||
if (constIndex != null) {
|
if (constIndex != null) {
|
||||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||||
if(arrayVar!=null) {
|
if(arrayVar!=null) {
|
||||||
val array =arrayVar.value as? ArrayLiteral
|
val array =arrayVar.value as? ArrayLiteral
|
||||||
if(array!=null) {
|
if(array!=null) {
|
||||||
@ -387,7 +387,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
val rangeTo = iterableRange.to as? NumericLiteral
|
val rangeTo = iterableRange.to as? NumericLiteral
|
||||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
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
|
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||||
require(loopvar.datatype.sub == null)
|
require(loopvar.datatype.sub == null)
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.FatalAstException
|
import prog8.ast.*
|
||||||
import prog8.ast.IFunctionCall
|
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.UndefinedSymbolError
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
@ -21,7 +16,12 @@ class VarConstantValueTypeAdjuster(
|
|||||||
private val errors: IErrorReporter
|
private val errors: IErrorReporter
|
||||||
) : AstWalker() {
|
) : AstWalker() {
|
||||||
|
|
||||||
private val callGraph by lazy { CallGraph(program) }
|
private lateinit var callGraph : CallGraph
|
||||||
|
|
||||||
|
override fun before(program: Program) : Iterable<IAstModification> {
|
||||||
|
callGraph = CallGraph(program)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
@ -56,15 +56,32 @@ class VarConstantValueTypeAdjuster(
|
|||||||
|| it.parent is AddressOf
|
|| it.parent is AddressOf
|
||||||
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
|
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
|
||||||
}
|
}
|
||||||
val singleAssignment =
|
|
||||||
writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
|
var singleAssignment: Assignment? = null
|
||||||
|
val singleWrite=writes.singleOrNull()
|
||||||
|
if(singleWrite!=null) {
|
||||||
|
singleAssignment = singleWrite.parent as? Assignment
|
||||||
|
if(singleAssignment==null) {
|
||||||
|
singleAssignment = singleWrite.parent.parent as? Assignment
|
||||||
|
if(singleAssignment==null) {
|
||||||
|
// we could be part of a multi-assign
|
||||||
|
if(singleWrite.parent is AssignTarget && singleWrite.parent.parent is AssignTarget)
|
||||||
|
singleAssignment = singleWrite.parent.parent.parent as? Assignment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (singleAssignment == null) {
|
if (singleAssignment == null) {
|
||||||
if (writes.isEmpty()) {
|
if (writes.isEmpty()) {
|
||||||
if(reads.isEmpty()) {
|
if(reads.isEmpty()) {
|
||||||
// variable is never used AT ALL so we just remove it altogether
|
if(decl.names.size>1) {
|
||||||
if("ignore_unused" !in decl.definingBlock.options())
|
errors.info("unused variable '${decl.name}'", decl.position)
|
||||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
} else {
|
||||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
// variable is never used AT ALL so we just remove it altogether
|
||||||
|
if ("ignore_unused" !in decl.definingBlock.options())
|
||||||
|
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val declValue = decl.value?.constValue(program)
|
val declValue = decl.value?.constValue(program)
|
||||||
if (declValue != null) {
|
if (declValue != null) {
|
||||||
@ -80,15 +97,20 @@ class VarConstantValueTypeAdjuster(
|
|||||||
} else {
|
} else {
|
||||||
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
|
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
|
||||||
if(reads.isEmpty()) {
|
if(reads.isEmpty()) {
|
||||||
// variable is never used AT ALL so we just remove it altogether, including the single assignment
|
if(decl.names.size>1) {
|
||||||
if("ignore_unused" !in decl.definingBlock.options())
|
errors.info("unused variable '${decl.name}'", decl.position)
|
||||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
} else {
|
||||||
return listOf(
|
// variable is never used AT ALL so we just remove it altogether, including the single assignment
|
||||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
if("ignore_unused" !in decl.definingBlock.options())
|
||||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||||
)
|
return listOf(
|
||||||
|
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||||
|
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
|
|
||||||
|
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, but only IF the value is a constant
|
||||||
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
|
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
|
||||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
|
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
@ -430,7 +452,7 @@ internal class ConstantIdentifierReplacer(
|
|||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||||
if(constRange!=null) {
|
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) {
|
return if(rangeType.isByte) {
|
||||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
if(truepart.statements.singleOrNull() is Jump) {
|
if(truepart.statements.singleOrNull() is Jump) {
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
|
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) {
|
if(elsepart.statements.singleOrNull() is Jump) {
|
||||||
@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
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)
|
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -531,7 +531,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
// useless msb() of byte value that was typecasted to word, replace with 0
|
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallExpr,
|
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))
|
parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -546,7 +546,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
// useless msb() of byte value, replace with 0
|
// useless msb() of byte value, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallExpr,
|
functionCallExpr,
|
||||||
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
|
NumericLiteral(argDt.getOr(DataType.UBYTE).base, 0.0, arg.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
|
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) {
|
if(target?.value is StringLiteral) {
|
||||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
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)
|
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||||
|
@ -23,9 +23,11 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
|
|||||||
|
|
||||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
var tries=0
|
||||||
|
while (errors.noErrors() && optimizer.applyModifications() > 0 && tries++ < 100000) {
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
}
|
}
|
||||||
|
require(tries<100000) { "endless loop in constantfold" }
|
||||||
|
|
||||||
if (errors.noErrors()) {
|
if (errors.noErrors()) {
|
||||||
replacer.visit(this)
|
replacer.visit(this)
|
||||||
|
@ -13,10 +13,10 @@ import prog8.code.core.InternalCompilerException
|
|||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
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() {
|
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||||
|
|
||||||
@ -38,26 +38,21 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
// subroutine is possible candidate to be inlined
|
// subroutine is possible candidate to be inlined
|
||||||
subroutine.inline =
|
subroutine.inline =
|
||||||
when (val stmt = subroutine.statements[0]) {
|
when (val stmt = subroutine.statements[0]) {
|
||||||
is Return -> {
|
// TODO consider multi-value returns as well
|
||||||
if (stmt.value is NumericLiteral)
|
is Return -> stmt.values.isEmpty() || stmt.values.size==1 &&
|
||||||
|
if (stmt.values[0] is NumericLiteral)
|
||||||
true
|
true
|
||||||
else if (stmt.value == null)
|
else if (stmt.values[0] is IdentifierReference) {
|
||||||
|
makeFullyScoped(stmt.values[0] as IdentifierReference)
|
||||||
true
|
true
|
||||||
else if (stmt.value 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 }) {
|
||||||
makeFullyScoped(stmt.value as IdentifierReference)
|
if (stmt.values[0] is FunctionCallExpression) {
|
||||||
true
|
makeFullyScoped(stmt.values[0] as FunctionCallExpression)
|
||||||
} else if (stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size <= 1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
true
|
||||||
when (stmt.value) {
|
|
||||||
is FunctionCallExpression -> {
|
|
||||||
makeFullyScoped(stmt.value as FunctionCallExpression)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
|
else false
|
||||||
} else
|
} else
|
||||||
false
|
false
|
||||||
}
|
|
||||||
|
|
||||||
is Assignment -> {
|
is Assignment -> {
|
||||||
if (stmt.value.isSimple) {
|
if (stmt.value.isSimple) {
|
||||||
@ -110,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
|
|
||||||
if (subroutine.inline && subroutine.statements.size > 1) {
|
if (subroutine.inline && subroutine.statements.size > 1) {
|
||||||
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||||
makeFullyScoped(call.target)
|
makeFullyScoped(call.target)
|
||||||
call.target.targetSubroutine(program)?.let { sub ->
|
call.target.targetSubroutine()?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
if(scopedArgs.any()) {
|
if(scopedArgs.any()) {
|
||||||
@ -139,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
makeFullyScoped(call.target)
|
makeFullyScoped(call.target)
|
||||||
call.target.targetSubroutine(program)?.let { sub ->
|
call.target.targetSubroutine()?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
if(scopedArgs.any()) {
|
if(scopedArgs.any()) {
|
||||||
@ -182,14 +177,19 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
when (val toInline = sub.statements.first()) {
|
when (val toInline = sub.statements.first()) {
|
||||||
is Return -> {
|
is Return -> {
|
||||||
val fcall = toInline.value as? FunctionCallExpression
|
// TODO consider multi-value returns as well
|
||||||
if(fcall!=null) {
|
if(toInline.values.size!=1)
|
||||||
// insert the function call expression as a void function call directly
|
|
||||||
sub.hasBeenInlined=true
|
|
||||||
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
|
||||||
} else
|
|
||||||
noModifications
|
noModifications
|
||||||
|
else {
|
||||||
|
val fcall = toInline.values[0] as? FunctionCallExpression
|
||||||
|
if(fcall!=null) {
|
||||||
|
// insert the function call expression as a void function call directly
|
||||||
|
sub.hasBeenInlined=true
|
||||||
|
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||||
|
} else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(origNode !== toInline) {
|
if(origNode !== toInline) {
|
||||||
@ -226,9 +226,10 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
is Return -> {
|
is Return -> {
|
||||||
// is an expression, so we have to have a Return here in the inlined sub
|
// is an expression, so we have to have a Return here in the inlined sub
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
if(toInline.value!=null && functionCallExpr!==toInline.value) {
|
// TODO consider multi-value returns as well
|
||||||
|
if(toInline.values.size==1 && functionCallExpr!==toInline.values[0]) {
|
||||||
sub.hasBeenInlined=true
|
sub.hasBeenInlined=true
|
||||||
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.values[0].copy(), parent))
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
noModifications
|
noModifications
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user