mirror of
https://github.com/irmen/prog8.git
synced 2024-06-01 06:41:42 +00:00
Compare commits
477 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8b10115390 | ||
|
d2e010c439 | ||
|
15867ab423 | ||
|
22c9e99fa3 | ||
|
ee262f6aad | ||
|
af64af2397 | ||
|
1feead2260 | ||
|
d3dcd24b4d | ||
|
fd1e6796ef | ||
|
3ea0f0cbaa | ||
|
f3e3311598 | ||
|
0dc50a93a4 | ||
|
fda8e61be4 | ||
|
ac1d4b4a7a | ||
|
c719e274d5 | ||
|
e4990f8ec5 | ||
|
62afd3342e | ||
|
6e8a89e6f1 | ||
|
aa2437cfb8 | ||
|
4a710ecdfc | ||
|
3ef5bdfeda | ||
|
7915dda35f | ||
|
9120e16683 | ||
|
a1ebc7090d | ||
|
054b4636e0 | ||
|
e3e7b060b7 | ||
|
5ac9c75521 | ||
|
07710e0995 | ||
|
d6a67f5f2b | ||
|
2675623aea | ||
|
94263c43d0 | ||
|
d8ec03874f | ||
|
a7247f5b8b | ||
|
4d37581694 | ||
|
5d7ddebcad | ||
|
53df0eb707 | ||
|
8babad9c7c | ||
|
8db7aa07bd | ||
|
42f4b06ac8 | ||
|
f4b50368ba | ||
|
db80417bd7 | ||
|
7a6f2ecc8c | ||
|
f5d556a7f9 | ||
|
2aae46d632 | ||
|
19ebc6d6b3 | ||
|
f88c29e083 | ||
|
6ed9899dc7 | ||
|
9de7698a5c | ||
|
112d2d6058 | ||
|
ddb8346711 | ||
|
8dd3faf395 | ||
|
35f3e8708b | ||
|
cfe3fcc9e7 | ||
|
66a6659a6e | ||
|
88ae3daa42 | ||
|
08b8fe01ab | ||
|
731132d4b3 | ||
|
98acff802f | ||
|
5f11f485a2 | ||
|
34f3169dda | ||
|
a3ef8f814b | ||
|
385dd6fc23 | ||
|
9af4168ae2 | ||
|
a5e0e31b74 | ||
|
b385dc8c26 | ||
|
92c012b55a | ||
|
641f6c05d8 | ||
|
788f6b44a6 | ||
|
63a4525f06 | ||
|
3e34a3ef72 | ||
|
0c5e8ca199 | ||
|
ff23fb0086 | ||
|
56f41d5e34 | ||
|
4700a239b9 | ||
|
bd5abfb969 | ||
|
b93fa75377 | ||
|
681ce9c60c | ||
|
dd0f0fe415 | ||
|
119040fc50 | ||
|
551e5688da | ||
|
56c1035581 | ||
|
ba1e907c79 | ||
|
2a3a27c56d | ||
|
647af34f5b | ||
|
993be6394e | ||
|
9a27505315 | ||
|
2e37f5dee3 | ||
|
03e486c082 | ||
|
edc83305a4 | ||
|
66e7c51064 | ||
|
60244aaf16 | ||
|
443391c700 | ||
|
47dbafacd4 | ||
|
5b6811d073 | ||
|
7516116bb7 | ||
|
e6014ea4dd | ||
|
362abfe284 | ||
|
ad4880997a | ||
|
592becc126 | ||
|
c38765301e | ||
|
5f27426f59 | ||
|
d924f8bff8 | ||
|
d14c61b160 | ||
|
fe2b67998c | ||
|
04df3c9f7f | ||
|
de3d0b40dc | ||
|
4db4a5f1b2 | ||
|
5a0524ff4d | ||
|
5b7801eea1 | ||
|
fbe231793b | ||
|
6a9269111e | ||
|
a94cfd34f5 | ||
|
28eae5a0fd | ||
|
1818738fc8 | ||
|
7e1e7a0780 | ||
|
1fc79ff6dd | ||
|
3535c1acda | ||
|
33c8caac8f | ||
|
51d708bbdd | ||
|
a5a918df84 | ||
|
820541e427 | ||
|
e63a8f0c01 | ||
|
c11a9b8709 | ||
|
80f39e8097 | ||
|
2a8b65e29c | ||
|
4bdf50145e | ||
|
3a9919a377 | ||
|
eef8ae00b8 | ||
|
ed15fac691 | ||
|
f739e679e4 | ||
|
fc0fae8caf | ||
|
f46896fd74 | ||
|
52649a8e4f | ||
|
bdfb01f6a0 | ||
|
1137e57393 | ||
|
267ea13e8c | ||
|
04f7b772a3 | ||
|
42c7569791 | ||
|
6d29b00a80 | ||
|
9f1bd2d7d6 | ||
|
9826d7c494 | ||
|
c6bf57b390 | ||
|
bfcf07c1a2 | ||
|
4d7e96d423 | ||
|
449461e412 | ||
|
607275ec66 | ||
|
e55cde2a81 | ||
|
84afb374e6 | ||
|
da1620807f | ||
|
f39ef8f565 | ||
|
fe8b6e820c | ||
|
f29d24e96a | ||
|
620ffe54ec | ||
|
ceaa4cd07d | ||
|
af17f903ee | ||
|
c532e28841 | ||
|
dba0846866 | ||
|
bed629998a | ||
|
bc2ede76bf | ||
|
2a1fec2ed2 | ||
|
004048e5a7 | ||
|
b941d6f1e4 | ||
|
37b346740b | ||
|
f5e332daf7 | ||
|
fe9a9fc5cb | ||
|
cc57477b99 | ||
|
a1574a7187 | ||
|
a5110b1f96 | ||
|
006713fe13 | ||
|
7868e672e0 | ||
|
e1a133c2c0 | ||
|
c77cd0da39 | ||
|
577333f2c4 | ||
|
7d8cdcbfea | ||
|
c5c4c6f111 | ||
|
73be754680 | ||
|
acd841dbb6 | ||
|
6b52ba9397 | ||
|
10d12f73d6 | ||
|
cd9119655c | ||
|
41afeccd51 | ||
|
6b87cbb703 | ||
|
32afcbfe42 | ||
|
bc2b38daf4 | ||
|
f40b7b62bb | ||
|
1ca3f64bf0 | ||
|
92527b4c1d | ||
|
c48012c385 | ||
|
a282b17286 | ||
|
58d9463f16 | ||
|
047decd552 | ||
|
82e0877e64 | ||
|
040d75dafa | ||
|
4e1686f6e3 | ||
|
b5e691f367 | ||
|
325f55f22d | ||
|
9724f2db7d | ||
|
5f20f321f0 | ||
|
d4b087ea3f | ||
|
8ff10724d1 | ||
|
1581381467 | ||
|
96b5a30f60 | ||
|
0e17a0474a | ||
|
b27368175d | ||
|
aba36f7c92 | ||
|
a3fa946300 | ||
|
01bbc2234e | ||
|
58e1864144 | ||
|
88458f5355 | ||
|
a4f697bae1 | ||
|
8201408f16 | ||
|
8b8caa1c2e | ||
|
4dc50cb551 | ||
|
5522a305ab | ||
|
d7f72056fc | ||
|
64c9c9b7fe | ||
|
98e1c843e4 | ||
|
906d9d858c | ||
|
16c1309df1 | ||
|
6eacf1bddd | ||
|
6c8c8e11cc | ||
|
e941d2665a | ||
|
68669dbef0 | ||
|
6a48de9a9f | ||
|
9d6d98930b | ||
|
3cc858db12 | ||
|
386a391fd9 | ||
|
d33aed4ed5 | ||
|
73ec8c31ad | ||
|
24944ad49e | ||
|
26ed231f61 | ||
|
8485b8429f | ||
|
358215e4dd | ||
|
f874942075 | ||
|
2cadb546d5 | ||
|
344a1b9eb8 | ||
|
3c77f8a020 | ||
|
8e00408e3e | ||
|
abcdfd8e28 | ||
|
b0f5b6925d | ||
|
ef79d0c43e | ||
|
78b4288005 | ||
|
680f5d21ee | ||
|
c71aa0895f | ||
|
9f8e61789a | ||
|
932035cdc5 | ||
|
ef198f1493 | ||
|
48ef856c0b | ||
|
9aea2b22c4 | ||
|
e0055bc431 | ||
|
9553248ed6 | ||
|
39d2194d8f | ||
|
0800033b47 | ||
|
64d8943b7d | ||
|
444e97b00b | ||
|
1816bda7ea | ||
|
d4a2031c07 | ||
|
8cf0b6cf51 | ||
|
f2010bf7a5 | ||
|
8f56a7fe69 | ||
|
64c132ee0a | ||
|
84a7e86fe3 | ||
|
a8c09d6144 | ||
|
87c46ba730 | ||
|
0f83dc6491 | ||
|
cc22861719 | ||
|
a14c192ea3 | ||
|
b3d98be862 | ||
|
43027a4728 | ||
|
03831a7394 | ||
|
fdbbd181ea | ||
|
69075376dc | ||
|
504d1440cc | ||
|
9e33b8b8da | ||
|
0cfcc5cd29 | ||
|
e0de662f8e | ||
|
66a836d094 | ||
|
80095f4962 | ||
|
828d83dbef | ||
|
7de665d1e4 | ||
|
0a356ba73a | ||
|
41de8caa13 | ||
|
968609d06d | ||
|
3b199a2a87 | ||
|
0c1018ec61 | ||
|
bc3f2db3de | ||
|
06bedb7adb | ||
|
45a9751217 | ||
|
e8da62aa29 | ||
|
ddb2ff4216 | ||
|
f27e3478b9 | ||
|
38dc7fb7bd | ||
|
aa4cd13c31 | ||
|
4a4b6c04a1 | ||
|
37fa3b34a2 | ||
|
f8084e7955 | ||
|
4d5119ce3e | ||
|
d85c347a6c | ||
|
7dd758a753 | ||
|
806654fc44 | ||
|
8e6b91cb9e | ||
|
334e6dca28 | ||
|
f2daa17b92 | ||
|
6d9fccacb1 | ||
|
37638e7ed0 | ||
|
8a0e650511 | ||
|
8ba5a0d90c | ||
|
bfd3edb617 | ||
|
56ba24962c | ||
|
19a2110ba2 | ||
|
242a3eec63 | ||
|
fee46f2e54 | ||
|
6aed7e429a | ||
|
517ea82b99 | ||
|
99c29343de | ||
|
892fa76883 | ||
|
d446b57d05 | ||
|
0e086d788b | ||
|
498841d45d | ||
|
d1f8ee1e56 | ||
|
07feb5c925 | ||
|
75fd263e85 | ||
|
1e1f444cab | ||
|
89cc7e5fa9 | ||
|
265e7aefbf | ||
|
1c55a6c6dc | ||
|
8f18b5b8a7 | ||
|
f790182f0b | ||
|
813007a5d8 | ||
|
d03ff1e4d0 | ||
|
932bbd0381 | ||
|
01bd648cb2 | ||
|
779a5606a7 | ||
|
ccc11e49d2 | ||
|
d28c994ecd | ||
|
5d88717f32 | ||
|
e35cfd4971 | ||
|
bcc4bf5c2b | ||
|
a0594cbce3 | ||
|
078bfefe41 | ||
|
9c1b11d605 | ||
|
44d82f9190 | ||
|
37fcde30d6 | ||
|
09c6cb4d6b | ||
|
b428343c2a | ||
|
dfce292294 | ||
|
2b8f613a00 | ||
|
2eb137618e | ||
|
4bb2b8ca9b | ||
|
5179562fb2 | ||
|
0a4de45453 | ||
|
ffdc658dc8 | ||
|
7530f4407b | ||
|
73864c8101 | ||
|
f948917124 | ||
|
0d44492086 | ||
|
38a22fbc99 | ||
|
8ae435549d | ||
|
9b113c0cbb | ||
|
0e0fac8c4b | ||
|
4cd9bb8f99 | ||
|
ad9eaeafeb | ||
|
6cd392909c | ||
|
49ec430592 | ||
|
09f3fbeb38 | ||
|
e7698686fa | ||
|
66d939df0d | ||
|
6bc079c7b7 | ||
|
299419917e | ||
|
69f6afe420 | ||
|
b7279a3d9e | ||
|
e14b854d7b | ||
|
8bd7c601c0 | ||
|
997288fa03 | ||
|
0f26b39997 | ||
|
ae66fcac1e | ||
|
43944a94eb | ||
|
eba0bde6f3 | ||
|
4544af441b | ||
|
a8be94de6b | ||
|
b24df31c2b | ||
|
332ba8ed7e | ||
|
58400f53bc | ||
|
01c2112881 | ||
|
a546c2247d | ||
|
0da9142009 | ||
|
796add0ee2 | ||
|
00b32f64e6 | ||
|
f97b3f23e2 | ||
|
08a079a96e | ||
|
e98e951834 | ||
|
2668bf8519 | ||
|
dd4c073e18 | ||
|
c7c72f00c7 | ||
|
ef1c665b9a | ||
|
d56565be25 | ||
|
e076b3aedc | ||
|
ae3b2ddf5f | ||
|
1bdc427d73 | ||
|
6a639ce533 | ||
|
d91ca8b197 | ||
|
a01c0a283d | ||
|
5c393091a0 | ||
|
01b680504b | ||
|
8e4319cd5a | ||
|
5a776dd690 | ||
|
cce08d95db | ||
|
28c1b208c1 | ||
|
3844bf1f72 | ||
|
745d192563 | ||
|
ee782e92ac | ||
|
afbc91d1fc | ||
|
f998888d6d | ||
|
7d8b42d63e | ||
|
6ebd4e821f | ||
|
d1806bfdc3 | ||
|
1d2d7155da | ||
|
b09e0a05bf | ||
|
c609e982fe | ||
|
2b227b43fe | ||
|
48f09f71ab | ||
|
ead8c59bda | ||
|
db52a9466c | ||
|
1509de390e | ||
|
88a1aa4f3d | ||
|
172e78e8f2 | ||
|
36bfef567d | ||
|
e40ebd75a2 | ||
|
992732f2cb | ||
|
b58a3ba1bb | ||
|
afe521b0c9 | ||
|
5d9caef45f | ||
|
278e2f5605 | ||
|
1e299bf360 | ||
|
8dfa0bc38c | ||
|
fde136fb7b | ||
|
ee4da1a757 | ||
|
ae2d96c455 | ||
|
6d8fbe0877 | ||
|
2fa1d8f2e8 | ||
|
533090a68e | ||
|
1dff59e1d6 | ||
|
44d232f52a | ||
|
5f6cff739a | ||
|
2764d235a9 | ||
|
45debff89f | ||
|
c45fbe6310 | ||
|
9ef9c24388 | ||
|
6a40f23578 | ||
|
6a0a6b4751 | ||
|
0bee6f6b41 | ||
|
82a15b5a16 | ||
|
11b7c4459e | ||
|
98570ac456 | ||
|
1b2296ad5b | ||
|
16851746d6 | ||
|
935450a45f | ||
|
ba67fd318b | ||
|
08ac459a41 | ||
|
a83e9d9a0a | ||
|
62d3f01948 | ||
|
af5ca2d0b8 | ||
|
ab4bcdf12d | ||
|
a6756d2cea | ||
|
f81061dd42 | ||
|
8e2c304b3c | ||
|
f21adaa3ef | ||
|
2637939e62 | ||
|
faf05582f8 | ||
|
161c02ced3 | ||
|
ff8de8e42d | ||
|
09d506194f | ||
|
42db3085df | ||
|
ad14c88fde | ||
|
0c9daf6eaf | ||
|
86c6530e46 | ||
|
159f80d629 |
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: irmen
|
||||
#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
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#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']
|
21
.github/workflows/all-ci.yml
vendored
21
.github/workflows/all-ci.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: build and install recent 64tass
|
||||
run: |
|
||||
|
@ -18,19 +18,24 @@ jobs:
|
|||
git clone --depth=1 https://github.com/irmen/64tass
|
||||
cd 64tass
|
||||
make -j4
|
||||
sudo make install
|
||||
sudo make install
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
distribution: temurin
|
||||
|
||||
- name: Build and test with Gradle
|
||||
run: ./gradlew build shadowJar --no-daemon
|
||||
|
||||
run: |
|
||||
./gradlew build shadowJar --no-daemon
|
||||
sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt
|
||||
|
||||
- name: Create compiler shadowJar artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prog8-compiler-jar-zipped
|
||||
path: compiler/build/libs/*-all.jar
|
||||
path: |
|
||||
compiler/build/libs/*-all.jar
|
||||
compiler/build/libs/hash.txt
|
||||
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -30,7 +30,6 @@ parsetab.py
|
|||
compiler/lib/
|
||||
|
||||
.gradle
|
||||
**/BuildVersion.kt
|
||||
/prog8compiler.jar
|
||||
sd*.img
|
||||
*.d64
|
||||
|
|
|
@ -9,6 +9,15 @@
|
|||
</inspection_tool>
|
||||
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="sphinx_rtd_theme" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="processCode" value="false" />
|
||||
<option name="processLiterals" value="true" />
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.20" />
|
||||
<option name="version" value="1.9.24" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24" />
|
||||
<CLASSES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.24/kotlin-stdlib-jdk7-1.9.24.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24-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/1.9.24/kotlin-stdlib-jdk7-1.9.24-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24-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/1.9.24/kotlin-stdlib-jdk7-1.9.24-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
|
@ -1,25 +0,0 @@
|
|||
<component name="libraryTable">
|
||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -1,10 +0,0 @@
|
|||
<component name="libraryTable">
|
||||
<library name="glassfish.javax.json" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -1,21 +1,18 @@
|
|||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.7.2" />
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.7.2/kotest-assertions-core-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.7.2/kotest-assertions-shared-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.0/kotest-assertions-core-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.0/kotest-assertions-shared-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.7.2/kotest-common-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.7.2/kotest-assertions-api-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.0/kotest-common-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.0/kotest-assertions-api-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.7.2" />
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.7.2/kotest-runner-junit5-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.7.2/kotest-framework-api-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.7.2/kotest-assertions-shared-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.9.0/kotest-runner-junit5-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.0/kotest-framework-api-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.0/kotest-assertions-shared-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.7.2/kotest-common-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.7.2/kotest-framework-engine-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.162/classgraph-4.8.162.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.0/kotest-common-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.0/kotest-framework-engine-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.7.2/kotest-framework-discovery-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.7.2/kotest-assertions-core-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.7.2/kotest-assertions-api-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.7.2/kotest-extensions-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.7.2/kotest-framework-concurrency-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.9.0/kotest-framework-discovery-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.0/kotest-assertions-core-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.0/kotest-assertions-api-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.0/kotest-extensions-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.0/kotest-framework-concurrency-jvm-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
|
||||
|
@ -32,11 +32,8 @@
|
|||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<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$/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/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<component name="libraryTable">
|
||||
<library name="slf4j.simple" type="repository">
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.9" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.9/slf4j-simple-2.0.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -1,18 +0,0 @@
|
|||
<component name="libraryTable">
|
||||
<library name="takes" type="repository">
|
||||
<properties maven-id="org.takes:takes:1.24.4" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -22,7 +22,7 @@
|
|||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -10,10 +10,8 @@
|
|||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/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$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
|
|
|
@ -9,17 +9,19 @@ version: 2
|
|||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
python: "3.12"
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
fail_on_warning: true
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
|
12
Makefile
Normal file
12
Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
# super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler
|
||||
|
||||
.PHONY: all test
|
||||
|
||||
all:
|
||||
gradle installdist installshadowdist
|
||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
|
||||
|
||||
test:
|
||||
gradle build
|
||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
|
||||
|
27
README.md
27
README.md
|
@ -1,3 +1,4 @@
|
|||
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF)
|
||||
[![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
|
@ -9,6 +10,13 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
|
|||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
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,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
|
@ -21,6 +29,8 @@ How to get it/build it
|
|||
- Or, if you want/need a bleeding edge development version, you can:
|
||||
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
|
||||
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
|
||||
- Alternatively, you can also install the compiler as a package on some linux distros:
|
||||
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
|
||||
|
||||
Community
|
||||
---------
|
||||
|
@ -41,8 +51,8 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
|||
What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- reduction of source code length over raw assembly
|
||||
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because compilation to native machine code
|
||||
- modularity, symbol scoping, subroutines
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
|
@ -50,6 +60,7 @@ What does Prog8 provide?
|
|||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with input parameters and result values
|
||||
- high-level program optimizations
|
||||
- no need for forward declarations
|
||||
- small program boilerplate/compilersupport overhead
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches
|
||||
|
@ -60,7 +71,7 @@ What does Prog8 provide?
|
|||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- 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, and provides them also on the C64.
|
||||
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
|
@ -108,14 +119,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
ubyte[256] sieve
|
||||
bool[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
sys.memset(sieve, 256, false) ; clear the sieve
|
||||
sys.memset(sieve, 256, 0) ; clear the sieve
|
||||
txt.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
repeat {
|
||||
|
@ -151,9 +161,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
when compiled an ran on a C-64 you'll get:
|
||||
|
||||
![c64 screen](docs/source/_static/primes_example.png)
|
||||
|
|
|
@ -6,9 +6,8 @@ plugins {
|
|||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -26,7 +25,7 @@ compileTestKotlin {
|
|||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
|
|
@ -82,8 +82,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||
override fun lookup(scopedName: String) = flat[scopedName]
|
||||
|
||||
fun getLength(name: String): Int? {
|
||||
val node = flat[name]
|
||||
return when(node) {
|
||||
return when(val node = flat[name]) {
|
||||
is StMemVar -> node.length
|
||||
is StMemorySlab -> node.size.toInt()
|
||||
is StStaticVariable -> node.length
|
||||
|
@ -195,7 +194,7 @@ class StStaticVariable(name: String,
|
|||
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||
}
|
||||
if(onetimeInitializationNumericValue!=null) {
|
||||
require(dt in NumericDatatypes)
|
||||
require(dt in NumericDatatypes || dt==DataType.BOOL)
|
||||
}
|
||||
if(onetimeInitializationArrayValue!=null) {
|
||||
require(dt in ArrayDatatypes)
|
||||
|
@ -221,6 +220,7 @@ class StMemVar(name: String,
|
|||
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||
|
||||
init{
|
||||
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
|
||||
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||
require(length!=null) { "memory mapped array or string must have known length" }
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ class StRomSub(name: String,
|
|||
|
||||
class StSubroutineParameter(val name: String, val type: DataType)
|
||||
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?)
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
||||
|
|
|
@ -128,10 +128,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name)
|
||||
StArrayElement(null, it.identifier.name, null)
|
||||
}
|
||||
is PtIdentifier -> StArrayElement(null, it.name)
|
||||
is PtNumber -> StArrayElement(it.number, null)
|
||||
is PtIdentifier -> StArrayElement(null, it.name, null)
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +175,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||
// }
|
||||
// VarDeclType.MEMORY -> {
|
||||
// val numElements =
|
||||
// if(decl.datatype in ArrayDatatypes)
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
|
|
|
@ -31,7 +31,10 @@ sealed class PtNode(val position: Position) {
|
|||
}
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY)
|
||||
sealed interface IPtStatementContainer
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
|
||||
|
||||
|
||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||
|
@ -64,27 +67,31 @@ class PtProgram(
|
|||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
|
||||
// returns the main.start subroutine if it exists
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
|
||||
?.children
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
class PtBlock(name: String,
|
||||
val address: UInt?,
|
||||
val library: Boolean,
|
||||
val forceOutput: Boolean,
|
||||
val noSymbolPrefixing: Boolean,
|
||||
val veraFxMuls: Boolean,
|
||||
val alignment: BlockAlignment,
|
||||
val source: SourceCode, // taken from the module the block is defined in.
|
||||
val options: Options,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position) {
|
||||
) : PtNamedNode(name, position), IPtStatementContainer {
|
||||
enum class BlockAlignment {
|
||||
NONE,
|
||||
WORD,
|
||||
PAGE
|
||||
}
|
||||
|
||||
class Options(val address: UInt? = null,
|
||||
val forceOutput: Boolean = false,
|
||||
val noSymbolPrefixing: Boolean = false,
|
||||
val veraFxMuls: Boolean = false,
|
||||
val ignoreUnused: Boolean = false,
|
||||
val alignment: BlockAlignment = BlockAlignment.NONE)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,14 +3,12 @@ package prog8.code.ast
|
|||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.round
|
||||
import kotlin.math.truncate
|
||||
|
||||
|
||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type==DataType.UNDEFINED) {
|
||||
@Suppress("LeakingThis")
|
||||
when(this) {
|
||||
|
@ -36,12 +34,20 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
||||
}
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||
is PtBinaryExpression -> {
|
||||
if(other !is PtBinaryExpression || other.operator!=operator)
|
||||
false
|
||||
else if(operator in AssociativeOperators)
|
||||
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
|
||||
else
|
||||
other.left isSameAs left && other.right isSameAs right
|
||||
}
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||
is PtIrRegister -> other is PtIrRegister && other.type==type && other.register==register
|
||||
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
||||
is PtBool -> other is PtBool && other.value==value
|
||||
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
|
||||
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
|
||||
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
|
||||
|
@ -64,7 +70,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
}
|
||||
}
|
||||
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
|
||||
|
||||
fun isSimple(): Boolean {
|
||||
return when(this) {
|
||||
|
@ -72,12 +78,18 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
is PtArray -> true
|
||||
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||
is PtBinaryExpression -> false
|
||||
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||
is PtBuiltinFunctionCall -> {
|
||||
when (name) {
|
||||
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is PtContainmentCheck -> false
|
||||
is PtFunctionCall -> false
|
||||
is PtIdentifier -> true
|
||||
is PtMachineRegister -> true
|
||||
is PtIrRegister -> true
|
||||
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
|
||||
is PtBool -> true
|
||||
is PtNumber -> true
|
||||
is PtPrefix -> value.isSimple()
|
||||
is PtRange -> true
|
||||
|
@ -104,6 +116,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
|
||||
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||
|
@ -129,15 +142,11 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
|||
get() = children[0] as PtIdentifier
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
val usesPointerVariable: Boolean
|
||||
get() = variable.type==DataType.UWORD
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
require(elementType in NumericDatatypesWithBoolean)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,15 +180,21 @@ class PtBuiltinFunctionCall(val name: String,
|
|||
|
||||
|
||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
|
||||
val left: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val right: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
init {
|
||||
if(operator in ComparisonOperators + LogicalOperators)
|
||||
require(type==DataType.BOOL)
|
||||
else
|
||||
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
|
||||
val element: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val iterable: PtIdentifier
|
||||
|
@ -191,11 +206,6 @@ class PtFunctionCall(val name: String,
|
|||
val void: Boolean,
|
||||
type: DataType,
|
||||
position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val args: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
}
|
||||
|
@ -216,6 +226,27 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
|
|||
}
|
||||
|
||||
|
||||
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
|
||||
|
||||
companion object {
|
||||
fun fromNumber(number: Number, position: Position): PtBool =
|
||||
PtBool(number != 0.0, position)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(type, value)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtBool)
|
||||
return false
|
||||
return value==other.value
|
||||
}
|
||||
|
||||
override fun toString() = "PtBool:$value"
|
||||
|
||||
fun asInt(): Int = if(value) 1 else 0
|
||||
}
|
||||
|
||||
|
||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||
|
||||
companion object {
|
||||
|
@ -225,11 +256,11 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
throw IllegalArgumentException("use PtBool instead")
|
||||
if(type!=DataType.FLOAT) {
|
||||
val rounded = round(number)
|
||||
if (rounded != number)
|
||||
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||
val trunc = truncate(number)
|
||||
if (trunc != number)
|
||||
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,7 +269,10 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtNumber)
|
||||
return false
|
||||
return number==other.number
|
||||
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
|
||||
return number==other.number
|
||||
else
|
||||
return type==other.type && number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||
|
@ -252,8 +286,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
|
|||
get() = children.single() as PtExpression
|
||||
|
||||
init {
|
||||
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,8 +345,8 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
|
|||
|
||||
|
||||
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
||||
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||
class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||
|
||||
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null
|
||||
|
|
|
@ -10,12 +10,17 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||
fun txt(node: PtNode): String {
|
||||
return when(node) {
|
||||
is PtAssignTarget -> "<target>"
|
||||
is PtAssignTarget -> if(node.void) "<void>" else "<target>"
|
||||
is PtAssignment -> "<assign>"
|
||||
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
|
||||
is PtBreakpoint -> "%breakpoint"
|
||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||
is PtAddressOf -> "&"
|
||||
is PtAddressOf -> {
|
||||
if(node.isFromArrayElement)
|
||||
"& array-element"
|
||||
else
|
||||
"&"
|
||||
}
|
||||
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
|
@ -29,12 +34,13 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
str + node.name + "()"
|
||||
}
|
||||
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
||||
is PtIrRegister -> "IRREG#${node.register} ${type(node.type)}"
|
||||
is PtMemoryByte -> "@()"
|
||||
is PtNumber -> {
|
||||
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
|
||||
"$numstr ${type(node.type)}"
|
||||
}
|
||||
is PtBool -> node.value.toString()
|
||||
is PtPrefix -> node.operator
|
||||
is PtRange -> "<range>"
|
||||
is PtString -> "\"${node.value.escape()}\""
|
||||
|
@ -53,15 +59,24 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
"goto ${node.identifier.name}"
|
||||
else if(node.address!=null)
|
||||
"goto ${node.address.toHex()}"
|
||||
else if(node.generatedLabel!=null)
|
||||
"goto ${node.generatedLabel}"
|
||||
else
|
||||
"???"
|
||||
}
|
||||
is PtAsmSub -> {
|
||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||
val params = node.parameters.joinToString(", ") {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
|
||||
}
|
||||
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||
val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
|
||||
val returns = if (node.returns.isEmpty()) "" else {
|
||||
"-> ${node.returns.joinToString(", ") {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second} @${register ?: statusflag}"
|
||||
}
|
||||
}"
|
||||
}
|
||||
val str = if (node.inline) "inline " else ""
|
||||
if(node.address==null) {
|
||||
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||
|
@ -70,12 +85,16 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
}
|
||||
}
|
||||
is PtBlock -> {
|
||||
val addr = if(node.address==null) "" else "@${node.address.toHex()}"
|
||||
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
|
||||
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}"
|
||||
val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}"
|
||||
"\nblock '${node.name}' $addr $align"
|
||||
}
|
||||
is PtConstant -> {
|
||||
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
|
||||
val value = when(node.type) {
|
||||
DataType.BOOL -> if(node.value==0.0) "false" else "true"
|
||||
in IntegerDatatypes -> node.value.toInt().toString()
|
||||
else -> node.value.toString()
|
||||
}
|
||||
"const ${node.type.name.lowercase()} ${node.name} = $value"
|
||||
}
|
||||
is PtLabel -> "${node.name}:"
|
||||
|
@ -89,7 +108,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||
val params = node.parameters.joinToString(", ") { "${it.type} ${it.name}" }
|
||||
var str = "sub ${node.name}($params) "
|
||||
if(node.returntype!=null)
|
||||
str += "-> ${node.returntype.name.lowercase()}"
|
||||
|
@ -112,9 +131,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
else
|
||||
str
|
||||
}
|
||||
is PtNodeGroup -> "<group>"
|
||||
is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else ""
|
||||
is PtNop -> "nop"
|
||||
is PtPostIncrDecr -> "<post> ${node.operator}"
|
||||
is PtProgram -> "PROGRAM ${node.name}"
|
||||
is PtRepeatLoop -> "repeat"
|
||||
is PtReturn -> "return"
|
||||
|
|
|
@ -23,13 +23,13 @@ class PtSub(
|
|||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine {
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
if(parameters.any{ it.type !in NumericDatatypes })
|
||||
throw AssemblyError("non-numeric parameter")
|
||||
if(returntype!=null && returntype !in NumericDatatypes)
|
||||
throw AssemblyError("non-numeric returntype $returntype")
|
||||
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
|
||||
throw AssemblyError("non-numeric/non-bool parameter")
|
||||
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
|
||||
throw AssemblyError("non-numeric/non-bool returntype $returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,18 @@ class PtSubroutineParameter(name: String, val type: DataType, position: Position
|
|||
sealed interface IPtAssignment {
|
||||
val children: MutableList<PtNode>
|
||||
val target: PtAssignTarget
|
||||
get() = children[0] as PtAssignTarget
|
||||
get() {
|
||||
if(children.size==2)
|
||||
return children[0] as PtAssignTarget
|
||||
else if(children.size<2)
|
||||
throw AssemblyError("incomplete node")
|
||||
else
|
||||
throw AssemblyError("no singular target")
|
||||
}
|
||||
val value: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
get() = children.last() as PtExpression
|
||||
val multiTarget: Boolean
|
||||
get() = children.size>2
|
||||
}
|
||||
|
||||
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
||||
|
@ -51,7 +60,7 @@ class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
|||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
|
||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
|
||||
val identifier: PtIdentifier?
|
||||
get() = children.single() as? PtIdentifier
|
||||
val array: PtArrayIndexer?
|
||||
|
@ -69,7 +78,7 @@ class PtAssignTarget(position: Position) : PtNode(position) {
|
|||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
|
||||
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,12 +107,13 @@ class PtIfElse(position: Position) : PtNode(position) {
|
|||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
|
||||
}
|
||||
|
||||
|
||||
class PtJump(val identifier: PtIdentifier?,
|
||||
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
|
||||
val address: UInt?,
|
||||
val generatedLabel: String?,
|
||||
position: Position) : PtNode(position) {
|
||||
init {
|
||||
identifier?.let {it.parent = this }
|
||||
|
@ -111,12 +121,6 @@ class PtJump(val identifier: PtIdentifier?,
|
|||
}
|
||||
|
||||
|
||||
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
||||
val target: PtAssignTarget
|
||||
get() = children.single() as PtAssignTarget
|
||||
}
|
||||
|
||||
|
||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||
val count: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
|
@ -153,7 +157,11 @@ class PtVariable(name: String, override val type: DataType, val zeropage: Zeropa
|
|||
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
|
|
|
@ -79,27 +79,28 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
|
||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||
// normal functions follow:
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
|
||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
||||
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||
"divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
|
||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||
|
@ -120,17 +121,21 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
|
||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
|
||||
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
|
||||
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
|
||||
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse")
|
||||
val InplaceModifyingBuiltinFunctions = setOf(
|
||||
"setlsb", "setmsb",
|
||||
"rol", "ror", "rol2", "ror2",
|
||||
"sort", "reverse",
|
||||
"divmod", "divmod__ubyte", "divmod__uword"
|
||||
)
|
||||
|
|
|
@ -19,10 +19,16 @@ class CompilationOptions(val output: OutputType,
|
|||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var dumpVariables: Boolean = false,
|
||||
var dumpSymbols: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var varsGolden: Boolean = false,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var breakpointCpuInstruction: Boolean = false,
|
||||
var strictBool: Boolean = true,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
|
|
|
@ -58,6 +58,7 @@ fun String.unescape(): String {
|
|||
'\\' -> '\\'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
't' -> '\t'
|
||||
'"' -> '"'
|
||||
'\'' -> '\''
|
||||
'u' -> {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package prog8.code.core
|
||||
|
||||
enum class DataType {
|
||||
UBYTE, // pass by value
|
||||
BYTE, // pass by value
|
||||
UWORD, // pass by value
|
||||
WORD, // pass by value
|
||||
FLOAT, // pass by value
|
||||
BOOL, // pass by value
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
BYTE, // pass by value 8 bits signed
|
||||
UWORD, // pass by value 16 bits unsigned
|
||||
WORD, // pass by value 16 bits signed
|
||||
LONG, // pass by value 32 bits signed
|
||||
FLOAT, // pass by value machine dependent
|
||||
BOOL, // pass by value bit 0 of a 8 bit byte
|
||||
STR, // pass by reference
|
||||
ARRAY_UB, // pass by reference
|
||||
ARRAY_B, // pass by reference
|
||||
|
@ -23,11 +24,12 @@ enum class DataType {
|
|||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(this) {
|
||||
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, FLOAT)
|
||||
BOOL -> targetType == BOOL
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
|
||||
LONG -> targetType.oneOf(LONG, FLOAT)
|
||||
FLOAT -> targetType.oneOf(FLOAT)
|
||||
STR -> targetType.oneOf(STR, UWORD)
|
||||
in ArrayDatatypes -> targetType == this
|
||||
|
@ -39,16 +41,17 @@ enum class DataType {
|
|||
infix fun largerThan(other: DataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this in ByteDatatypes -> false
|
||||
this in WordDatatypes -> other in ByteDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> false
|
||||
this in ByteDatatypesWithBoolean -> false
|
||||
this in WordDatatypes -> other in ByteDatatypesWithBoolean
|
||||
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
infix fun equalsSize(other: DataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this in ByteDatatypes -> other in ByteDatatypes
|
||||
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
|
||||
this in WordDatatypes -> other in WordDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
else -> false
|
||||
|
@ -75,7 +78,7 @@ enum class RegisterOrPair {
|
|||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
|
||||
return when(cpu) {
|
||||
CpuRegister.A -> A
|
||||
|
@ -101,7 +104,7 @@ enum class Statusflag {
|
|||
Pn; // don't use
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,17 +120,18 @@ enum class BranchCondition {
|
|||
PL, // PL == POS
|
||||
POS,
|
||||
VS,
|
||||
VC,
|
||||
VC
|
||||
}
|
||||
|
||||
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
|
||||
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
|
||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
|
||||
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
|
||||
|
@ -138,7 +142,7 @@ val IterableDatatypes = arrayOf(
|
|||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||
)
|
||||
val PassByValueDatatypes = NumericDatatypes
|
||||
val PassByValueDatatypes = NumericDatatypesWithBoolean
|
||||
val PassByReferenceDatatypes = IterableDatatypes
|
||||
val ArrayToElementTypes = mapOf(
|
||||
DataType.STR to DataType.UBYTE,
|
||||
|
@ -157,7 +161,8 @@ val ElementToArrayTypes = mapOf(
|
|||
DataType.WORD to DataType.ARRAY_W,
|
||||
DataType.UWORD to DataType.ARRAY_UW,
|
||||
DataType.FLOAT to DataType.ARRAY_F,
|
||||
DataType.BOOL to DataType.ARRAY_BOOL
|
||||
DataType.BOOL to DataType.ARRAY_BOOL,
|
||||
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
|
||||
)
|
||||
|
||||
val Cx16VirtualRegisters = arrayOf(
|
||||
|
|
|
@ -3,8 +3,6 @@ package prog8.code.core
|
|||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
val name: String
|
||||
val machine: IMachineDefinition
|
||||
val supportedEncodings: Set<Encoding>
|
||||
val defaultEncoding: Encoding
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
|
|
|
@ -3,11 +3,14 @@ package prog8.code.core
|
|||
interface IErrorReporter {
|
||||
fun err(msg: String, position: Position)
|
||||
fun warn(msg: String, position: Position)
|
||||
fun info(msg: String, position: Position)
|
||||
fun undefined(symbol: List<String>, position: Position)
|
||||
fun noErrors(): Boolean
|
||||
fun report()
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||
if(numErrors>0)
|
||||
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
|
||||
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ interface IMachineDefinition {
|
|||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
|
@ -24,6 +26,9 @@ interface IMachineDefinition {
|
|||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
|
|
@ -5,10 +5,15 @@ enum class Encoding(val prefix: String) {
|
|||
PETSCII("petscii"), // c64/c128/cx16
|
||||
SCREENCODES("sc"), // c64/c128/cx16
|
||||
ATASCII("atascii"), // atari
|
||||
ISO("iso") // cx16
|
||||
ISO("iso"), // cx16 (iso-8859-15)
|
||||
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||
CP437("cp437") // cx16 (ibm pc, codepage 437)
|
||||
}
|
||||
|
||||
interface IStringEncoding {
|
||||
val defaultEncoding: Encoding
|
||||
|
||||
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||
|
||||
val size: Int =
|
||||
when (datatype) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
|
@ -119,7 +119,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||
free.removeAll(address until address+size.toUInt())
|
||||
if(name.isNotEmpty()) {
|
||||
allocatedVariables[name] = when(datatype) {
|
||||
in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
DataType.STR -> VarAllocation(address, datatype, size)
|
||||
in ArrayDatatypes -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
|
@ -151,7 +151,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||
|
||||
val size: Int =
|
||||
when (datatype) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
options.compTarget.memorySize(datatype, numElements!!)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||
|
||||
fun invertedComparisonOperator(operator: String) =
|
||||
when (operator) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package prog8.code.core
|
|||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
@ -68,7 +69,8 @@ sealed class SourceCode {
|
|||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `string:44c56085`.
|
||||
*/
|
||||
class Text(override val text: String): SourceCode() {
|
||||
class Text(origText: String): SourceCode() {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||
|
@ -94,7 +96,8 @@ sealed class SourceCode {
|
|||
val normalized = path.normalize()
|
||||
origin = relative(normalized).toString()
|
||||
try {
|
||||
text = normalized.readText()
|
||||
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = normalized.toFile().nameWithoutExtension
|
||||
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||
|
@ -126,7 +129,8 @@ sealed class SourceCode {
|
|||
)
|
||||
}
|
||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||
text = stream!!.reader().use { it.readText() }
|
||||
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = Path(pathString).toFile().nameWithoutExtension
|
||||
}
|
||||
}
|
||||
|
|
212
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
212
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
|
@ -0,0 +1,212 @@
|
|||
package prog8.code.optimize
|
||||
|
||||
import prog8.code.StRomSub
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||
if (!options.optimize)
|
||||
return
|
||||
while (errors.noErrors() &&
|
||||
(optimizeCommonSubExpressions(program, errors)
|
||||
+ optimizeAssignTargets(program, st, errors)) > 0
|
||||
) {
|
||||
// keep rolling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
fun recurse(node: PtNode, depth: Int) {
|
||||
if(act(node, depth))
|
||||
node.children.forEach { recurse(it, depth+1) }
|
||||
}
|
||||
recurse(root, 0)
|
||||
}
|
||||
|
||||
|
||||
private var tempVarCounter = 0
|
||||
|
||||
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
|
||||
|
||||
fun extractableSubExpr(expr: PtExpression): Boolean {
|
||||
if(expr is PtArrayIndexer && expr.index.isSimple())
|
||||
return false
|
||||
if (expr is PtMemoryByte && expr.address.isSimple())
|
||||
return false
|
||||
|
||||
val result = if(expr is PtBinaryExpression)
|
||||
expr.type !in ByteDatatypes ||
|
||||
!(expr.left.isSimple() && expr.right.isSimple()) ||
|
||||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
|
||||
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
|
||||
true
|
||||
else
|
||||
!expr.isSimple()
|
||||
return result
|
||||
}
|
||||
|
||||
// for each Binaryexpression, recurse to find a common subexpression pair therein.
|
||||
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtBinaryExpression) {
|
||||
val subExpressions = mutableListOf<PtExpression>()
|
||||
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
outer@for (first in subExpressions) {
|
||||
for (second in subExpressions) {
|
||||
if (first!==second && first isSameAs second) {
|
||||
commons[node] = first to second
|
||||
break@outer // do only 1 replacement at a time per binaryexpression
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
} else true
|
||||
}
|
||||
|
||||
// replace common subexpressions by a temp variable that is assigned only once.
|
||||
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
|
||||
commons.forEach { binexpr, (occurrence1, occurrence2) ->
|
||||
val (stmtContainer, stmt) = findContainingStatements(binexpr)
|
||||
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
|
||||
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
|
||||
val containerScopedName = findScopeName(stmtContainer)
|
||||
tempVarCounter++
|
||||
val tempvarName = "prog8_subexprvar_$tempVarCounter"
|
||||
// TODO: some tempvars could be reused, if they are from different lines
|
||||
|
||||
val datatype = occurrence1.type
|
||||
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
|
||||
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
|
||||
occurrence1.parent.children[occurrence1idx] = singleReplacement1
|
||||
singleReplacement1.parent = occurrence1.parent
|
||||
occurrence2.parent.children[occurrence2idx] = singleReplacement2
|
||||
singleReplacement2.parent = occurrence2.parent
|
||||
|
||||
val tempassign = PtAssignment(binexpr.position).also { assign ->
|
||||
assign.add(PtAssignTarget(false, binexpr.position).also { tgt->
|
||||
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
|
||||
})
|
||||
assign.add(occurrence1)
|
||||
occurrence1.parent = assign
|
||||
}
|
||||
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
|
||||
tempassign.parent = stmtContainer
|
||||
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
|
||||
stmtContainer.add(0, tempvar)
|
||||
tempvar.parent = stmtContainer
|
||||
|
||||
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
|
||||
}
|
||||
|
||||
return commons.size
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
|
||||
var changes = 0
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtAssignment) {
|
||||
val value = node.value
|
||||
val functionName = when(value) {
|
||||
is PtBuiltinFunctionCall -> value.name
|
||||
is PtFunctionCall -> value.name
|
||||
else -> null
|
||||
}
|
||||
if(functionName!=null) {
|
||||
val stNode = st.lookup(functionName)
|
||||
if (stNode is StRomSub) {
|
||||
require(node.children.size==stNode.returns.size+1) {
|
||||
"number of targets must match return values"
|
||||
}
|
||||
node.children.zip(stNode.returns).withIndex().forEach { (index, xx) ->
|
||||
val target = xx.first as PtAssignTarget
|
||||
val returnedRegister = xx.second.register.registerOrPair
|
||||
if(returnedRegister!=null && !target.void && target.identifier!=null) {
|
||||
if(isSame(target.identifier!!, xx.second.type, returnedRegister)) {
|
||||
// output register is already identical to target register, so it can become void
|
||||
val voidTarget = PtAssignTarget(true, target.position)
|
||||
node.children[index] = voidTarget
|
||||
voidTarget.parent = node
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
|
||||
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
|
||||
val index = node.parent.children.indexOf(node)
|
||||
val voidCall = PtFunctionCall(functionName, true, value.type, value.position)
|
||||
value.children.forEach { voidCall.add(it) }
|
||||
node.parent.children[index] = voidCall
|
||||
voidCall.parent = node.parent
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
|
||||
if(returnedRegister in Cx16VirtualRegisters) {
|
||||
val regname = returnedRegister.name.lowercase()
|
||||
val identifierRegName = identifier.name.substringAfterLast('.')
|
||||
/*
|
||||
cx16.r? UWORD
|
||||
cx16.r?s WORD
|
||||
cx16.r?L UBYTE
|
||||
cx16.r?H UBYTE
|
||||
cx16.r?sL BYTE
|
||||
cx16.r?sH BYTE
|
||||
*/
|
||||
if(identifier.type in ByteDatatypes && type in ByteDatatypes) {
|
||||
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
|
||||
}
|
||||
}
|
||||
else if(identifier.type in WordDatatypes && type in WordDatatypes) {
|
||||
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||
return identifierRegName.substring(2) in arrayOf("", "s")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false // there are no identifiers directly corresponding to cpu registers
|
||||
}
|
||||
|
||||
|
||||
internal fun findScopeName(node: PtNode): String {
|
||||
var parent=node
|
||||
while(parent !is PtNamedNode)
|
||||
parent = parent.parent
|
||||
return parent.scopedName
|
||||
}
|
||||
|
||||
|
||||
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
|
||||
var parent = node.parent
|
||||
var child = node
|
||||
while(true) {
|
||||
if(parent is IPtStatementContainer) {
|
||||
return parent to child
|
||||
}
|
||||
child=parent
|
||||
parent=parent.parent
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ import prog8.code.target.atari.AtariMachineDefinition
|
|||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = AtariMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.ATASCII)
|
||||
override val defaultEncoding = Encoding.ATASCII
|
||||
|
||||
companion object {
|
||||
|
@ -16,13 +15,17 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
if(arrayDt==DataType.UWORD)
|
||||
numElements // pointer to bytes.
|
||||
else
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import prog8.code.target.cbm.CbmMemorySizer
|
|||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = C128MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -11,7 +11,6 @@ import prog8.code.target.cbm.CbmMemorySizer
|
|||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = C64MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
|
@ -20,3 +19,23 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Cb
|
|||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val CompilationTargets = listOf(
|
||||
C64Target.NAME,
|
||||
C128Target.NAME,
|
||||
Cx16Target.NAME,
|
||||
PETTarget.NAME,
|
||||
AtariTarget.NAME,
|
||||
VMTarget.NAME
|
||||
)
|
||||
|
||||
fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
|
||||
C64Target.NAME -> C64Target()
|
||||
C128Target.NAME -> C128Target()
|
||||
Cx16Target.NAME -> Cx16Target()
|
||||
PETTarget.NAME -> PETTarget()
|
||||
AtariTarget.NAME -> AtariTarget()
|
||||
VMTarget.NAME -> VMTarget()
|
||||
else -> throw IllegalArgumentException("invalid compilation target")
|
||||
}
|
|
@ -11,7 +11,6 @@ import prog8.code.target.cx16.CX16MachineDefinition
|
|||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = CX16MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -4,18 +4,21 @@ import com.github.michaelbull.result.fold
|
|||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.target.cbm.AtasciiEncoding
|
||||
import prog8.code.target.cbm.IsoEncoding
|
||||
import prog8.code.target.cbm.PetsciiEncoding
|
||||
import prog8.code.target.encodings.*
|
||||
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return coded.fold(
|
||||
|
@ -29,6 +32,9 @@ object Encoder: IStringEncoding {
|
|||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return decoded.fold(
|
||||
|
|
|
@ -11,7 +11,6 @@ import prog8.code.target.pet.PETMachineDefinition
|
|||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = PETMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -6,7 +6,6 @@ import prog8.code.target.virtual.VirtualMachineDefinition
|
|||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = VirtualMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.ISO)
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
|
||||
companion object {
|
||||
|
@ -15,13 +14,16 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
if(arrayDt==DataType.UWORD)
|
||||
numElements // pointer to bytes.
|
||||
else
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
|
@ -15,11 +15,15 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.output == OutputType.XEX)
|
||||
|
|
|
@ -17,12 +17,25 @@ class C128MachineDefinition: IMachineDefinition {
|
|||
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
|
|
|
@ -17,13 +17,26 @@ class C64MachineDefinition: IMachineDefinition {
|
|||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xd000u
|
||||
override val BSSHIGHRAM_END = 0xcfffu
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
|
|
|
@ -6,13 +6,16 @@ import prog8.code.core.*
|
|||
internal object CbmMemorySizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
if(arrayDt==DataType.UWORD)
|
||||
numElements // pointer to bytes.
|
||||
else
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
|
@ -16,12 +16,26 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
|
@ -36,7 +50,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = emptyList()
|
||||
extraArgs = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
|
@ -49,7 +63,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
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()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
|
@ -0,0 +1,69 @@
|
|||
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
|
||||
import java.nio.charset.Charset
|
||||
|
||||
object Cp437Encoding {
|
||||
val charset: Charset = Charset.forName("IBM437")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 255u
|
||||
'☺' -> 1u
|
||||
'☻' -> 2u
|
||||
'♥' -> 3u
|
||||
'♦' -> 4u
|
||||
'♣' -> 5u
|
||||
'♠' -> 6u
|
||||
'•' -> 7u
|
||||
'◘' -> 8u
|
||||
'○' -> 9u
|
||||
'◙' -> 10u
|
||||
'♂' -> 11u
|
||||
'♀' -> 12u
|
||||
'♪' -> 13u
|
||||
'♫' -> 14u
|
||||
'☼' -> 15u
|
||||
'►' -> 16u
|
||||
'◄' -> 17u
|
||||
'↕' -> 18u
|
||||
'‼' -> 19u
|
||||
'¶' -> 20u
|
||||
'§' -> 21u
|
||||
'▬' -> 22u
|
||||
'↨' -> 23u
|
||||
'↑' -> 24u
|
||||
'↓' -> 25u
|
||||
'→' -> 26u
|
||||
'←' -> 27u
|
||||
'∟' -> 28u
|
||||
'↔' -> 29u
|
||||
'▲' -> 30u
|
||||
'▼' -> 31u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> charset.encode(chr.toString())[0].toUByte()
|
||||
}
|
||||
}
|
||||
Ok(mapped)
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
|
@ -6,8 +6,8 @@ import com.github.michaelbull.result.Result
|
|||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
object IsoEncoding {
|
||||
val charset: Charset = Charset.forName("ISO-8859-15")
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
|
@ -35,3 +35,8 @@ object IsoEncoding {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object IsoEncoding: IsoEncodingBase("ISO-8859-15")
|
||||
object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5")
|
||||
object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16")
|
|
@ -1,4 +1,4 @@
|
|||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
|
@ -15,14 +15,27 @@ class PETMachineDefinition: IMachineDefinition {
|
|||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xffffu
|
||||
override val BSSHIGHRAM_END = 0xffffu
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
|
|
|
@ -12,22 +12,44 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||
|
||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||
override val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // 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.toFloat().toBits().toUInt()
|
||||
val hexStr = bits.toString(16).padStart(8, '0')
|
||||
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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return listOf("syslib")
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "io.kotest" version "0.3.9"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -28,9 +25,11 @@ dependencies {
|
|||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -58,15 +58,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeSamePointerIndexing(linesByFourteen)
|
||||
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
// TODO more assembly peephole optimizations
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
|
@ -75,23 +73,48 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
|
|||
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
|
||||
|
||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||
if(modification.remove)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
if(modification.remove) {
|
||||
if(modification.removeLabel)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else {
|
||||
val line = lines[modification.lineIndex]
|
||||
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else if (haslabel(line)) {
|
||||
val label = keeplabel(line)
|
||||
if (label.isNotEmpty())
|
||||
lines[modification.lineIndex] = label
|
||||
else
|
||||
lines.removeAt(modification.lineIndex)
|
||||
} else lines.removeAt(modification.lineIndex)
|
||||
}
|
||||
}
|
||||
else
|
||||
lines[modification.lineIndex] = modification.replacement!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun haslabel(line: String): Boolean {
|
||||
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
|
||||
}
|
||||
|
||||
private fun keeplabel(line: String): String {
|
||||
if(':' in line)
|
||||
return line.substringBefore(':') + ':'
|
||||
val splits = line.split('\t', ' ', limit=2)
|
||||
return if(splits.size>1) splits[0] + ':' else ""
|
||||
}
|
||||
|
||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
|
@ -281,7 +304,7 @@ private fun optimizeSameAssignments(
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
|
||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||
// if Y isn't modified in between we can omit the second LDY:
|
||||
|
@ -318,31 +341,57 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
|
|||
mods.add(Modification(lines[4].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
beq +
|
||||
lda #1
|
||||
+
|
||||
[ optional: label_xxxx_shortcut line here]
|
||||
beq label_xxxx_shortcut / bne label_xxxx_shortcut
|
||||
or *_afterif labels.
|
||||
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
|
||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
|
||||
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeStoreLoadSame(
|
||||
linesByFour: List<List<IndexedValue<String>>>,
|
||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
|
||||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||
(first.startsWith("ldx ") && second.startsWith("ldx "))
|
||||
) {
|
||||
val third = lines[3].value.trimStart()
|
||||
val attemptRemove =
|
||||
|
@ -381,15 +430,21 @@ private fun optimizeStoreLoadSame(
|
|||
} else if(first=="phx" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txa"))
|
||||
} else if(first=="phx" && second=="ply") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txy"))
|
||||
} else if(first=="phy" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tya"))
|
||||
} else if(first=="phy" && second=="plx") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tyx"))
|
||||
}
|
||||
|
||||
|
||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
|
@ -420,7 +475,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
|||
}
|
||||
}
|
||||
|
||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
|
@ -437,14 +492,17 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
|
|||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
|
@ -476,11 +534,54 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
|
|||
else if (" bvc" in second || "\tbvc" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
|
||||
if (!haslabel(second)) {
|
||||
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
|
||||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
|
||||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
|
||||
) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
else if(" cmp #0" in second || "\tcmp #0" in second) {
|
||||
// there are many instructions that modify A and set the bits...
|
||||
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
|
||||
if(" $instr" in first || "\t$instr" in first) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
LDA NUM1
|
||||
CMP NUM2
|
||||
BCC LABEL
|
||||
BEQ LABEL
|
||||
|
||||
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
|
||||
|
||||
LDA NUM2
|
||||
CMP NUM1
|
||||
BCS LABEL
|
||||
*/
|
||||
val tfirst = first.trimStart()
|
||||
val tsecond = second.trimStart()
|
||||
val tthird = lines[2].value.trimStart()
|
||||
val tfourth = lines[3].value.trimStart()
|
||||
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
|
||||
val label = tthird.substring(4)
|
||||
if(label==tfourth.substring(4)) {
|
||||
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
|
||||
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
|
||||
mods += Modification(lines[2].index, false, " bcs $label")
|
||||
mods += Modification(lines[3].index, true, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
||||
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
|
||||
|
@ -517,7 +618,7 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
||||
// this can be performed without the scratch variable: clc / adc something
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
@ -529,8 +630,8 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<Str
|
|||
val fourth = lines[3].value.trimStart()
|
||||
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
|
||||
if(fourth.substring(4)==first.substring(4)) {
|
||||
mods.add(Modification(lines[0].index, false, " clc"))
|
||||
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
||||
mods.add(Modification(lines[0].index, false, " clc"))
|
||||
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ internal class AssemblyProgram(
|
|||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
|
@ -39,8 +39,9 @@ internal class AssemblyProgram(
|
|||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
if(options.asmListfile) {
|
||||
command.addAll(listOf("--list=$listFile", "--no-monitor"))
|
||||
}
|
||||
|
||||
val outFile = when (options.output) {
|
||||
OutputType.PRG -> {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
@ -47,8 +48,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
"reverse" -> funcReverse(fcall)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
"pokemon" -> {
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
memread.add(fcall.args[0])
|
||||
|
@ -60,47 +63,125 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
asmgen.out(" pla")
|
||||
}
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
|
||||
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
|
||||
"pop" -> {
|
||||
require(fcall.args[0] is PtIdentifier) {
|
||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||
}
|
||||
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||
val target = symbol!!.astNode as IPtVariable
|
||||
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
|
||||
}
|
||||
"popw" -> {
|
||||
require(fcall.args[0] is PtIdentifier) {
|
||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||
}
|
||||
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||
val target = symbol!!.astNode as IPtVariable
|
||||
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
|
||||
}
|
||||
"rsave" -> funcRsave()
|
||||
"rrestore" -> funcRrestore()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD)
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
|
||||
"prog8_lib_arraycopy" -> funcArrayCopy(fcall)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||
}
|
||||
|
||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||
}
|
||||
|
||||
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType) {
|
||||
private fun funcArrayCopy(fcall: PtBuiltinFunctionCall) {
|
||||
val source = fcall.args[0] as PtIdentifier
|
||||
val target = fcall.args[1] as PtIdentifier
|
||||
|
||||
val numElements = when(val sourceSymbol = asmgen.symbolTable.lookup(source.name)) {
|
||||
is StStaticVariable -> sourceSymbol.length!!
|
||||
is StMemVar -> sourceSymbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
val sourceAsm = asmgen.asmVariableName(source)
|
||||
val targetAsm = asmgen.asmVariableName(target)
|
||||
|
||||
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
|
||||
// split -> split words (copy lsb and msb arrays separately)
|
||||
asmgen.out("""
|
||||
lda #<${sourceAsm}_lsb
|
||||
ldy #>${sourceAsm}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${targetAsm}_lsb
|
||||
ldy #>${targetAsm}_lsb
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #${numElements and 255}
|
||||
jsr prog8_lib.memcopy_small
|
||||
lda #<${sourceAsm}_msb
|
||||
ldy #>${sourceAsm}_msb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${targetAsm}_msb
|
||||
ldy #>${targetAsm}_msb
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #${numElements and 255}
|
||||
jsr prog8_lib.memcopy_small""")
|
||||
}
|
||||
else if(source.type in SplitWordArrayTypes) {
|
||||
// split word array to normal word array (copy lsb and msb arrays separately)
|
||||
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
|
||||
asmgen.out("""
|
||||
lda #<${sourceAsm}_lsb
|
||||
ldy #>${sourceAsm}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${sourceAsm}_msb
|
||||
ldy #>${sourceAsm}_msb
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda #<${targetAsm}
|
||||
ldy #>${targetAsm}
|
||||
ldx #${numElements and 255}
|
||||
jsr prog8_lib.arraycopy_split_to_normal_words""")
|
||||
}
|
||||
else if(target.type in SplitWordArrayTypes) {
|
||||
// normal word array to split array
|
||||
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
|
||||
asmgen.out("""
|
||||
lda #<${targetAsm}_lsb
|
||||
ldy #>${targetAsm}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${targetAsm}_msb
|
||||
ldy #>${targetAsm}_msb
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda #<${sourceAsm}
|
||||
ldy #>${sourceAsm}
|
||||
ldx #${numElements and 255}
|
||||
jsr prog8_lib.arraycopy_normal_to_split_words""")
|
||||
}
|
||||
else {
|
||||
// normal array to array copy, various element types
|
||||
val eltsize = asmgen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
|
||||
val numBytes = numElements * eltsize
|
||||
asmgen.out("""
|
||||
lda #<${sourceAsm}
|
||||
ldy #>${sourceAsm}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${targetAsm}
|
||||
ldy #>${targetAsm}
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #${numBytes and 255}
|
||||
jsr prog8_lib.memcopy_small""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
|
||||
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
|
||||
when (resultType) {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
|
||||
asmgen.out(" tay | jsr math.multiply_bytes")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr math.square")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("optimized square only for integer types")
|
||||
|
@ -140,9 +221,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
sty $remainderVar+1""")
|
||||
}
|
||||
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall) {
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
|
||||
asmgen.out(" jsr prog8_lib.strcmp_mem")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcRsave() {
|
||||
|
@ -182,7 +266,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
plp""")
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
|
||||
private fun funcCall(fcall: PtBuiltinFunctionCall) {
|
||||
val constAddr = fcall.args[0].asConstInteger()
|
||||
if(constAddr!=null) {
|
||||
asmgen.out(" jsr ${constAddr.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) // jump address
|
||||
asmgen.out("""
|
||||
sta (+)+1
|
||||
sty (+)+2
|
||||
+ jsr 0 ; modified""")
|
||||
}
|
||||
|
||||
// note: the routine can return a word value (in AY)
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
if(asmgen.options.compTarget.name != "cx16")
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
|
||||
|
@ -196,6 +295,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
+ .word 0
|
||||
+ .byte 0""")
|
||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCmp(fcall: PtBuiltinFunctionCall) {
|
||||
|
@ -212,6 +314,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp #${arg2.number.toInt()}")
|
||||
}
|
||||
is PtBool -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp #${arg2.asInt()}")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if(arg2.address is PtNumber) {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
|
@ -240,6 +346,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
cmp ${asmgen.asmVariableName(arg2)}
|
||||
+""")
|
||||
}
|
||||
is PtBool -> TODO("word compare against bool")
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
|
@ -297,106 +404,112 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
|
||||
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
|
||||
val variable = fcall.args.single()
|
||||
if (variable is PtIdentifier) {
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("cannot reverse a constant")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val variable = fcall.args.single() as PtIdentifier
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
when (dt) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
when (decl.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_w""")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr floats.func_reverse_f""")
|
||||
}
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_w""")
|
||||
}
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #${numElements-1}
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr floats.func_reverse_f""")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
// reverse the lsb and msb arrays both, independently
|
||||
asmgen.out("""
|
||||
lda #<${varName}_lsb
|
||||
ldy #>${varName}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b
|
||||
lda #<${varName}_msb
|
||||
ldy #>${varName}_msb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcSort(fcall: PtBuiltinFunctionCall) {
|
||||
val variable = fcall.args.single()
|
||||
if (variable is PtIdentifier) {
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("cannot sort a constant")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val variable = fcall.args.single() as PtIdentifier
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
when (dt) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (dt == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
||||
}
|
||||
when (decl.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
in SplitWordArrayTypes -> TODO("split word sort")
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (dt == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("weird type")
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #${numElements-1}
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
in SplitWordArrayTypes -> TODO("split words sort")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
|
||||
|
@ -405,8 +518,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'b')
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #\$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
|
@ -427,10 +541,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("ror2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
|
@ -449,8 +565,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "ror", 'b')
|
||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
|
@ -459,15 +578,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
} 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
|
||||
|
@ -485,10 +608,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("ror split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
asmgen.out(" ror ${varname}+1,x | ror ${varname},x")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
|
@ -507,8 +634,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'b')
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | cmp #\$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
|
@ -529,10 +657,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("rol2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
||||
else
|
||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
|
@ -551,8 +681,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "rol", 'b')
|
||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
|
@ -561,15 +694,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
} 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
|
||||
|
@ -587,10 +724,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("rol split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
asmgen.out(" rol ${varname},x | rol ${varname}+1,x")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
|
@ -603,23 +744,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
||||
if(indexer.splitWords)
|
||||
TODO("rol/ror split words access ${indexer.position}")
|
||||
if(arrayvar.type==DataType.UWORD) {
|
||||
if(dt!='b')
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
} else {
|
||||
val p = arrayvar.parent
|
||||
val addressOf = PtAddressOf(arrayvar.position)
|
||||
addressOf.add(arrayvar)
|
||||
addressOf.parent = p
|
||||
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
}
|
||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun funcSetLsbMsb(fcall: PtBuiltinFunctionCall, msb: Boolean) {
|
||||
val target: AsmAssignTarget
|
||||
when(fcall.args[0]) {
|
||||
|
@ -635,6 +759,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
is PtAddressOf -> {
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
if((fcall.args[0] as PtAddressOf).isFromArrayElement)
|
||||
TODO("address-of arrayelement")
|
||||
if(msb) {
|
||||
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
|
||||
address.add(fcall.args[0])
|
||||
|
@ -647,7 +773,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
require(!indexer.usesPointerVariable)
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
|
@ -701,8 +826,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
|
||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
when (dt) {
|
||||
when (val dt = fcall.args.single().type) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
|
@ -714,13 +838,57 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().type
|
||||
val array = fcall.args[0] as PtIdentifier
|
||||
when (dt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr floats.func_${fcall.name}_f_into_A")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
val numElements = (asmgen.symbolTable.lookup(array.name) as StStaticVariable).length
|
||||
when(fcall.name) {
|
||||
"any" -> {
|
||||
// any(lsb-array) or any(msb-array)
|
||||
val arrayName = asmgen.asmVariableName(array)
|
||||
asmgen.out("""
|
||||
lda #<${arrayName}_lsb
|
||||
ldy #>${arrayName}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
""")
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
asmgen.out(" bne +") // shortcircuit
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${arrayName}_msb
|
||||
ldy #>${arrayName}_msb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
""")
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
pla
|
||||
ora P8ZP_SCRATCH_REG
|
||||
+""")
|
||||
}
|
||||
"all" -> {
|
||||
TODO("split words all")
|
||||
}
|
||||
else -> throw AssemblyError("weird call")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes, true)
|
||||
|
@ -752,6 +920,40 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
private fun funcPokeF(fcall: PtBuiltinFunctionCall) {
|
||||
when(val number = fcall.args[1]) {
|
||||
is PtIdentifier -> {
|
||||
val varName = asmgen.asmVariableName(number)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>$varName
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.assignConstFloatToPointerAY(number)
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<$tempvar
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>$tempvar
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
|
@ -808,6 +1010,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
private fun funcPeekF(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
|
@ -1129,15 +1340,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVar,y")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.X)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
|
||||
asmgen.out(" lda $arrayVar,x")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" ldx $arrayVar,y")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
|
@ -1183,24 +1394,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
|
||||
// address in P8ZP_SCRATCH_W1, number of elements in A
|
||||
arg as PtIdentifier
|
||||
val symbol = asmgen.symbolTable.lookup(arg.name)
|
||||
val arrayVar = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(arrayVar) {
|
||||
is PtConstant -> null
|
||||
is PtMemMapped -> arrayVar.arraySize
|
||||
is PtVariable -> arrayVar.arraySize
|
||||
} ?: throw AssemblyError("length of non-array requested")
|
||||
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
|
||||
// address goes in P8ZP_SCRATCH_W1, number of elements in A
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(arg.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
val identifierName = asmgen.asmVariableName(arg)
|
||||
asmgen.out("""
|
||||
lda #<$identifierName
|
||||
ldy #>$identifierName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
""")
|
||||
lda #${numElements and 255}""")
|
||||
}
|
||||
|
||||
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
|
||||
|
|
|
@ -17,7 +17,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
|||
emptyList()
|
||||
else {
|
||||
val register = when (returntype!!) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
|
@ -31,7 +31,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
|||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when(returntype) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
null -> null
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.PtForLoop
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtRange
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
|
@ -331,12 +335,10 @@ $endLabel""")
|
|||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val symbol = asmgen.symbolTable.lookup(ident.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("length of non-array requested")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
when(iterableDt) {
|
||||
DataType.STR -> {
|
||||
|
@ -356,7 +358,7 @@ $loopLabel lda ${65535.toHex()} ; modified
|
|||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
|
@ -364,7 +366,7 @@ $loopLabel sty $indexVar
|
|||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements!!<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
|
@ -379,7 +381,7 @@ $loopLabel sty $indexVar
|
|||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@ -392,7 +394,7 @@ $loopLabel sty $indexVar
|
|||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
val length = numElements!! * 2u
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
|
@ -403,7 +405,7 @@ $loopLabel sty $indexVar
|
|||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127u) {
|
||||
if(length<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
|
@ -420,7 +422,7 @@ $loopLabel sty $indexVar
|
|||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16u) {
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@ -433,7 +435,6 @@ $loopLabel sty $indexVar
|
|||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
||||
numElements!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
|
@ -444,7 +445,7 @@ $loopLabel sty $indexVar
|
|||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
|
@ -459,7 +460,7 @@ $loopLabel sty $indexVar
|
|||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
|
|
@ -16,8 +16,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
}
|
||||
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
|
||||
|
||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
|
@ -26,7 +26,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
val sub = symbol?.astNode as IPtSubroutine
|
||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||
|
||||
if(sub is PtAsmSub) {
|
||||
|
@ -46,7 +46,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaRegisters(sub)) {
|
||||
if(sub.parameters.size==1) {
|
||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||
} else {
|
||||
// 2 byte params, second in Y, first in A
|
||||
|
@ -81,15 +81,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
}
|
||||
is PtAddressOf -> false
|
||||
is PtIdentifier -> false
|
||||
is PtMachineRegister -> false
|
||||
is PtMemoryByte -> false
|
||||
is PtIrRegister -> false
|
||||
is PtMemoryByte -> return usesOtherRegistersWhileEvaluating(arg.address)
|
||||
is PtNumber -> false
|
||||
is PtBool -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
|
||||
val registersUsed = mutableListOf<RegisterOrStatusflag>();
|
||||
val registersUsed = mutableListOf<RegisterOrStatusflag>()
|
||||
|
||||
fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY}
|
||||
fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY}
|
||||
|
@ -152,7 +153,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
}
|
||||
if (statusflag!=null) {
|
||||
if(requiredDt!=value.type)
|
||||
throw AssemblyError("for statusflag, byte value is required")
|
||||
throw AssemblyError("for statusflag, byte or bool value is required")
|
||||
if (statusflag == Statusflag.Pc) {
|
||||
// this boolean param needs to be set last, right before the jsr
|
||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||
|
@ -161,6 +162,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
val carrySet = value.number.toInt() != 0
|
||||
asmgen.out(if(carrySet) " sec" else " clc")
|
||||
}
|
||||
is PtBool -> {
|
||||
asmgen.out(if(value.value) " sec" else " clc")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
// note: cannot use X register here to store A because it might be used for other arguments
|
||||
|
@ -218,6 +222,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType==DataType.BOOL && paramType==DataType.BOOL)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
return true
|
||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||
|
|
1759
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
1759
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,175 +0,0 @@
|
|||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtNumber
|
||||
import prog8.code.ast.PtPostIncrDecr
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
internal fun translate(stmt: PtPostIncrDecr) {
|
||||
val incr = stmt.operator=="++"
|
||||
val targetIdent = stmt.target.identifier
|
||||
val targetMemory = stmt.target.memory
|
||||
val targetArrayIdx = stmt.target.array
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
when (stmt.target.type) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $what | bne + | inc $what+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $what
|
||||
bne +
|
||||
dec $what+1
|
||||
+ dec $what
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$what | ldy #>$what")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
targetMemory!=null -> {
|
||||
when (val addressExpr = targetMemory.address) {
|
||||
is PtNumber -> {
|
||||
val what = addressExpr.number.toHex()
|
||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val what = asmgen.asmVariableName(addressExpr)
|
||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
}
|
||||
}
|
||||
targetArrayIdx!=null -> {
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
||||
val elementDt = targetArrayIdx.type
|
||||
val constIndex = targetArrayIdx.index.asConstInteger()
|
||||
if(targetArrayIdx.splitWords) {
|
||||
if(constIndex!=null) {
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb+$constIndex
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb+$constIndex
|
||||
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb,x
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb,x
|
||||
+ dec ${asmArrayvarname}_lsb,x""")
|
||||
}
|
||||
return
|
||||
}
|
||||
if(constIndex!=null) {
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname
|
||||
clc
|
||||
adc #$indexValue
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
txa
|
||||
clc
|
||||
adc $asmArrayvarname
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
+ dec $asmArrayvarname,x""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
ldy #>$asmArrayvarname
|
||||
clc
|
||||
adc #<$asmArrayvarname
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.inc_var_f""")
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,8 +34,8 @@ internal class ProgramAndVarsGen(
|
|||
header()
|
||||
val allBlocks = program.allBlocks()
|
||||
|
||||
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main")
|
||||
throw AssemblyError("first block should be 'main' or 'p8_main'")
|
||||
if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main")
|
||||
throw AssemblyError("first block should be 'main' or 'p8b_main'")
|
||||
|
||||
if(errors.noErrors()) {
|
||||
program.allBlocks().forEach { block2asm(it) }
|
||||
|
@ -49,6 +49,7 @@ internal class ProgramAndVarsGen(
|
|||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
|
@ -133,29 +134,22 @@ internal class ProgramAndVarsGen(
|
|||
pha""")
|
||||
}
|
||||
|
||||
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
|
||||
when(compTarget.name) {
|
||||
"cx16" -> {
|
||||
if(options.floats)
|
||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||
asmgen.out(" jsr p8_main.p8_start")
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
else -> asmgen.jmp("p8_main.p8_start")
|
||||
else -> asmgen.jmp("p8b_main.p8s_start")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,48 +166,120 @@ 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) {
|
||||
DataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||
DataType.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() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
var relocatedBssEnd = 0u
|
||||
|
||||
if(options.varsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.varsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
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)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
if(options.slabsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.slabsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
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)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
asmgen.out("; bss sections")
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(options.varsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
|
||||
}
|
||||
// BSS vars in high ram area, memory() slabs just concatenated at the end of the program.
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
}
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
// BSS vars followed by memory() slabs, concatenated at the end of the program.
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
}
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun block2asm(block: PtBlock) {
|
||||
asmgen.out("")
|
||||
asmgen.out("; ---- block: '${block.name}' ----")
|
||||
if(block.address!=null)
|
||||
asmgen.out("* = ${block.address!!.toHex()}")
|
||||
if(block.options.address!=null)
|
||||
asmgen.out("* = ${block.options.address!!.toHex()}")
|
||||
else {
|
||||
if(block.alignment==PtBlock.BlockAlignment.WORD)
|
||||
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
|
||||
asmgen.out("\t.align 2")
|
||||
else if(block.alignment==PtBlock.BlockAlignment.PAGE)
|
||||
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
|
||||
asmgen.out("\t.align $100")
|
||||
}
|
||||
|
||||
asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block" else ".proc"))
|
||||
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
|
||||
asmgen.outputSourceLine(block)
|
||||
|
||||
createBlockVariables(block)
|
||||
|
@ -236,7 +302,7 @@ internal class ProgramAndVarsGen(
|
|||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
|
||||
asmgen.out(if(block.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||
}
|
||||
|
||||
private fun getVars(scope: StNode): Map<String, StNode> =
|
||||
|
@ -276,7 +342,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.forceOutput) {
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
|
@ -299,7 +365,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.forceOutput) {
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
|
@ -331,7 +397,7 @@ internal class ProgramAndVarsGen(
|
|||
asmsubs2asm(sub.children)
|
||||
|
||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main"))
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||
|
@ -339,7 +405,7 @@ internal class ProgramAndVarsGen(
|
|||
if(sub.parameters.size==1) {
|
||||
val dt = sub.parameters[0].type
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||
if(dt in ByteDatatypes)
|
||||
if(dt in ByteDatatypesWithBoolean)
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
else
|
||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||
|
@ -539,7 +605,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||
|
@ -569,7 +635,7 @@ internal class ProgramAndVarsGen(
|
|||
} else 0
|
||||
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
|
@ -593,7 +659,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
|
||||
when(dt) {
|
||||
DataType.ARRAY_UB -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
||||
|
@ -661,7 +727,7 @@ internal class ProgramAndVarsGen(
|
|||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null))
|
||||
values.add(StArrayElement(0.0, null, null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
@ -698,6 +764,16 @@ internal class ProgramAndVarsGen(
|
|||
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
return when (dt) {
|
||||
DataType.ARRAY_BOOL ->
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
array.map {
|
||||
if(it.boolean!=null)
|
||||
if(it.boolean==true) "1" else "0"
|
||||
else {
|
||||
val number = it.number!!
|
||||
if(number==0.0) "0" else "1"
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB ->
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
array.map {
|
||||
|
|
|
@ -48,9 +48,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||
val numberOfAllocatableVariables = allVariables.size
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
|
||||
val numberOfExplicitNonZpVariables = allVariables.count { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
|
||||
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
var numberOfNonIntegerVariables = 0
|
||||
|
@ -86,12 +86,12 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||
// no need to check for allocation error, if there is one, just allocate in normal system ram.
|
||||
}
|
||||
|
||||
// try to allocate any other interger variables into the zeropage until it is full.
|
||||
// try to allocate the "don't care" interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt in IntegerDatatypes) {
|
||||
if(variable.dt in IntegerDatatypesWithBoolean) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
|
@ -110,9 +110,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||
}
|
||||
}
|
||||
|
||||
// println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||
// println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||
// note: no zeropage allocation is done at all for the @nozp variables. This means they will always end up outside the zeropage.
|
||||
}
|
||||
|
||||
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||
|
|
|
@ -18,24 +18,24 @@ internal class AnyExprAsmGen(
|
|||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
|
||||
return assignByteBinExpr(expr, assign)
|
||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
throw AssemblyError("words operands comparison -> byte, should have been handled by assignOptimizedComparisonWords()")
|
||||
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
|
||||
}
|
||||
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
throw AssemblyError("weird expr operand types: ${expr.left.type} and {${expr.right.type}")
|
||||
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
"both operands must be words"
|
||||
}
|
||||
return assignWordBinExpr(expr)
|
||||
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
|
@ -47,60 +47,6 @@ internal class AnyExprAsmGen(
|
|||
}
|
||||
}
|
||||
|
||||
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
TODO("word + at ${expr.position}")
|
||||
}
|
||||
"-" -> {
|
||||
TODO("word - at ${expr.position}")
|
||||
}
|
||||
"*" -> {
|
||||
TODO("word * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("word / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("word << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("word >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("word % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
TODO("word and at ${expr.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
TODO("word or at ${expr.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
TODO("word xor at ${expr.position}")
|
||||
}
|
||||
"==" -> {
|
||||
TODO("word == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("word != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("word < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("word <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("word > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("word >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
|
@ -119,22 +65,14 @@ internal class AnyExprAsmGen(
|
|||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
TODO("byte * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("byte / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("byte << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("byte >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("byte % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
"*" -> TODO("byte * at ${expr.position}")
|
||||
"/" -> TODO("byte / at ${expr.position}")
|
||||
"<<" -> TODO("byte << at ${expr.position}")
|
||||
">>" -> TODO("byte >> at ${expr.position}")
|
||||
"%" -> TODO("byte % at ${expr.position}")
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
|
@ -142,7 +80,7 @@ internal class AnyExprAsmGen(
|
|||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|", "or" -> {
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
|
@ -158,24 +96,12 @@ internal class AnyExprAsmGen(
|
|||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
TODO("byte == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("byte != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("byte < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("byte <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("byte > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("byte >= at ${expr.position}")
|
||||
}
|
||||
"==" -> TODO("byte == at ${expr.position}")
|
||||
"!=" -> TODO("byte != at ${expr.position}")
|
||||
"<" -> TODO("byte < at ${expr.position}")
|
||||
"<=" -> TODO("byte <= at ${expr.position}")
|
||||
">" -> TODO("byte > at ${expr.position}")
|
||||
">=" -> TODO("byte >= at ${expr.position}")
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ internal enum class TargetStorageKind {
|
|||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
LITERALBOOLEAN,
|
||||
LITERALNUMBER,
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
|
@ -43,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||
}
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
throw AssemblyError("register must be integer or float type")
|
||||
if(register!=null && datatype !in NumericDatatypesWithBoolean)
|
||||
throw AssemblyError("must be numeric type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -133,6 +134,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val number: PtNumber? = null,
|
||||
val boolean: PtBool? = null,
|
||||
val expression: PtExpression? = null
|
||||
)
|
||||
{
|
||||
|
@ -147,6 +149,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
val cv = value as? PtNumber
|
||||
if(cv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
val bv = value as? PtBool
|
||||
if(bv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
|
@ -194,7 +199,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
// allow some signed/unsigned relaxations
|
||||
|
||||
fun withAdjustedDt(newType: DataType) =
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
|
||||
|
||||
if(target.datatype!=datatype) {
|
||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@ import prog8.code.core.*
|
|||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
DataType.FLOAT -> 5
|
||||
else -> 2
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ internal object DummyMemsizer : IMemSizer {
|
|||
}
|
||||
|
||||
internal object DummyStringEncoder : IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -27,11 +29,11 @@ internal object DummyStringEncoder : IStringEncoding {
|
|||
}
|
||||
}
|
||||
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||
IErrorReporter {
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
|
||||
|
||||
val errors = mutableListOf<String>()
|
||||
val warnings = mutableListOf<String>()
|
||||
val infos = mutableListOf<String>()
|
||||
|
||||
override fun err(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
|
@ -45,17 +47,25 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
warnings.add(text)
|
||||
}
|
||||
|
||||
override fun info(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
if(text !in infos)
|
||||
infos.add(text)
|
||||
}
|
||||
|
||||
override fun undefined(symbol: List<String>, position: Position) {
|
||||
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||
}
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||
if(throwExceptionAtReportIfErrors)
|
||||
finalizeNumErrors(errors.size, warnings.size)
|
||||
finalizeNumErrors(errors.size, warnings.size, infos.size)
|
||||
if(!keepMessagesAfterReporting) {
|
||||
clear()
|
||||
}
|
||||
|
@ -64,5 +74,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
fun clear() {
|
||||
errors.clear()
|
||||
warnings.clear()
|
||||
infos.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class TestCodegen: FunSpec({
|
|||
//}
|
||||
val codegen = AsmGen6502(prefixSymbols = false)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
|
@ -53,7 +53,7 @@ class TestCodegen: FunSpec({
|
|||
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(Position.DUMMY).also {
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
|
||||
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||
|
@ -68,7 +68,7 @@ class TestCodegen: FunSpec({
|
|||
sub.add(assign)
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
|
@ -76,7 +76,7 @@ class TestCodegen: FunSpec({
|
|||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
|
@ -84,7 +84,7 @@ class TestCodegen: FunSpec({
|
|||
sub.add(numberAssign)
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
|
@ -95,7 +95,7 @@ class TestCodegen: FunSpec({
|
|||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@ plugins {
|
|||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -29,7 +28,7 @@ dependencies {
|
|||
implementation project(':codeGenIntermediate')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@ plugins {
|
|||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -28,9 +27,12 @@ dependencies {
|
|||
implementation project(':intermediate')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,7 @@
|
|||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.SplitWordArrayTypes
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
|
@ -20,19 +17,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
|
||||
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
|
||||
"pop" -> funcPop(call)
|
||||
"popw" -> funcPopw(call)
|
||||
"push" -> funcPush(call)
|
||||
"pushw" -> funcPushw(call)
|
||||
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"callfar" -> funcCallfar(call)
|
||||
"call" -> funcCall(call)
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
"peek" -> funcPeek(call)
|
||||
"peekw" -> funcPeekW(call)
|
||||
"poke" -> funcPoke(call)
|
||||
"pokew" -> funcPokeW(call)
|
||||
"peek" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
"mkword" -> funcMkword(call)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
|
||||
|
@ -42,17 +38,79 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
"reverse" -> funcReverse(call)
|
||||
"setlsb" -> funcSetLsbMsb(call, false)
|
||||
"setmsb" -> funcSetLsbMsb(call, true)
|
||||
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||
"ror" -> funcRolRor(Opcode.ROXR, call)
|
||||
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||
"rol" -> funcRolRor(call)
|
||||
"ror" -> funcRolRor(call)
|
||||
"rol2" -> funcRolRor(call)
|
||||
"ror2" -> funcRolRor(call)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_arraycopy" -> funcArrayCopy(call)
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val source = call.args[0] as PtIdentifier
|
||||
val target = call.args[1] as PtIdentifier
|
||||
val sourceLength = codeGen.symbolTable.getLength(source.name)!!
|
||||
val targetLength = codeGen.symbolTable.getLength(target.name)!!
|
||||
require(sourceLength==targetLength)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val fromReg = codeGen.registers.nextFree()
|
||||
val toReg = codeGen.registers.nextFree()
|
||||
val countReg = codeGen.registers.nextFree()
|
||||
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
|
||||
// split words -> split words, copy lsb and msb arrays separately
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
|
||||
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
|
||||
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
|
||||
}
|
||||
}
|
||||
else if(source.type in SplitWordArrayTypes) {
|
||||
// split -> normal words
|
||||
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
|
||||
val fromRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromRegMsb, labelSymbol = source.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
|
||||
}
|
||||
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to fromRegMsb, IRDataType.WORD to toReg, IRDataType.BYTE to countReg), returns = null)
|
||||
}
|
||||
else if(target.type in SplitWordArrayTypes) {
|
||||
// normal -> split words
|
||||
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
|
||||
val toRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toRegMsb, labelSymbol = target.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
|
||||
}
|
||||
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to toRegMsb, IRDataType.BYTE to countReg), returns = null)
|
||||
}
|
||||
else {
|
||||
// normal array to array copy (various element types)
|
||||
val eltsize = codeGen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize)
|
||||
}
|
||||
result += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
|
@ -69,6 +127,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
}
|
||||
}
|
||||
|
||||
private fun funcCall(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null)
|
||||
if(call.void)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
|
||||
}
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
|
@ -138,6 +207,31 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// any(lsb-array) or any(msb-array)
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trLsb, trLsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
|
||||
val shortcircuitLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
|
||||
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
|
||||
}
|
||||
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trMsb, trMsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
|
||||
result += IRCodeChunk(shortcircuitLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when (arrayName.type) {
|
||||
DataType.ARRAY_UB,
|
||||
|
@ -147,12 +241,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
|
||||
else -> throw IllegalArgumentException("weird type")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
|
@ -160,6 +252,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
|
||||
TODO("all(split words $arrayName)")
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when(arrayName.type) {
|
||||
DataType.ARRAY_UB,
|
||||
|
@ -171,10 +269,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
|
@ -221,14 +319,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
|
||||
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val vmDt = irType(call.type)
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
|
||||
if(tr.dt==IRDataType.FLOAT) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, fpReg1 = tr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
|
@ -260,64 +365,40 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
}
|
||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt for sqrt")
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val reg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||
result += assignRegisterTo(call.args.single(), reg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
|
||||
}
|
||||
|
||||
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val reg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||
result += assignRegisterTo(call.args.single(), reg)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
|
||||
}
|
||||
|
||||
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// reverse the lsb and msb arrays both, independently
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trLsb, trLsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trMsb, trMsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when(arrayName.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
||||
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw IllegalArgumentException("weird type to reverse")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
|
@ -339,7 +420,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
|
@ -440,116 +521,93 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
private fun funcPoke(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
|
||||
it += IRInstruction(Opcode.STOREZM, dt, address = address)
|
||||
}
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZI, dt, reg1 = tr.resultReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
val tr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address)
|
||||
if(dt==IRDataType.FLOAT) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, dt, fpReg1 = tr.resultFpReg, address = address)
|
||||
}
|
||||
} else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, dt, reg1 = tr.resultReg, address = address)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||
if(dt==IRDataType.FLOAT) {
|
||||
addToResult(result, valueTr, -1, valueTr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, IRDataType.FLOAT, reg1 = addressTr.resultReg, fpReg1 = valueTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, dt, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcPoke(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
private fun funcPeek(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
if (call.args[0] is PtNumber) {
|
||||
return if(dt==IRDataType.FLOAT) {
|
||||
if(call.args[0] is PtNumber) {
|
||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpRegister)
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||
}
|
||||
} else {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
val tr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address)
|
||||
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, dt, resultRegister, -1)
|
||||
} else {
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
ExpressionCodeResult(result, dt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcPeekW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
return if(call.args[0] is PtNumber) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeek(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
return if(call.args[0] is PtNumber) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPokemon(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
|
@ -616,7 +674,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return exprGen.translateExpression(call.args.single())
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
|
||||
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
|
@ -624,22 +682,58 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
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.
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val vmDt = irType(call.args[0].type)
|
||||
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
|
||||
val arg = call.args[0]
|
||||
val vmDt = irType(arg.type)
|
||||
val opcodeMemAndReg = when(call.name) {
|
||||
"rol" -> Opcode.ROXLM to Opcode.ROXL
|
||||
"ror" -> Opcode.ROXRM to Opcode.ROXR
|
||||
"rol2" -> Opcode.ROLM to Opcode.ROL
|
||||
"ror2" -> Opcode.RORM to Opcode.ROR
|
||||
else -> throw AssemblyError("wrong func")
|
||||
}
|
||||
result += assignRegisterTo(call.args[0], tr.resultReg)
|
||||
|
||||
val ident = arg as? PtIdentifier
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = ident.name), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val memAddr = (arg as? PtMemoryByte)?.address?.asConstInteger()
|
||||
if(memAddr!=null) {
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, address = memAddr), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
val variable = arr.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val opcode = opcodeMemAndReg.second
|
||||
val saveCarry = opcode in OpcodesThatDependOnCarry && !arg.isSimple()
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
|
||||
val tr = exprGen.translateExpression(arg)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.POPST), null)
|
||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
|
||||
result += assignRegisterTo(arg, tr.resultReg)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.POPST), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
|
@ -670,7 +764,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
}
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
require(!target.usesPointerVariable)
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
|
@ -761,10 +854,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||
|
||||
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
||||
val assignment = PtAssignment(target.position)
|
||||
val assignTarget = PtAssignTarget(target.position)
|
||||
val assignTarget = PtAssignTarget(false, target.position)
|
||||
assignTarget.children.add(target)
|
||||
assignment.children.add(assignTarget)
|
||||
assignment.children.add(PtMachineRegister(register, target.type, target.position))
|
||||
assignment.children.add(PtIrRegister(register, target.type, target.position))
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
result += codeGen.translateNode(assignment)
|
||||
return result
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -46,21 +46,18 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
.map { IndexedValue(it.index, it.value) }
|
||||
val changed = removeNops(chunk1, indexedInstructions)
|
||||
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions)
|
||||
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|
||||
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|
||||
|| removeUselessArithmetic(chunk1, indexedInstructions)
|
||||
|| removeNeedlessCompares(chunk1, indexedInstructions)
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
// TODO other optimizations
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
removeEmptyChunks(sub)
|
||||
}
|
||||
|
||||
// TODO also do register optimization step here at the end?
|
||||
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
|
@ -87,7 +84,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: merge both labels into 1)
|
||||
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
|
@ -96,6 +93,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
|
||||
val relabelChunks = mutableListOf<Pair<Int, String>>()
|
||||
val removeChunks = mutableListOf<Int>()
|
||||
val replaceLabels = mutableMapOf<String, String>()
|
||||
|
||||
sub.chunks.withIndex().forEach { (index, chunk) ->
|
||||
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
|
||||
|
@ -109,10 +107,18 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
relabelChunks += Pair(index + 1, chunk.label!!)
|
||||
removeChunks += index
|
||||
} else {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
// TODO: merge labels on same chunk
|
||||
// merge both labels into 1 except if this is the label chunk at the start of the subroutine
|
||||
if(index>0) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
removeChunks += index
|
||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
||||
replaceLabels.entries.forEach { (key, value) ->
|
||||
if (value == chunk.label)
|
||||
replaceLabels[key] = nextchunk.label!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +135,25 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
sub.chunks[index] = chunk
|
||||
}
|
||||
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
|
||||
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.instructions.withIndex().forEach { (idx, instr) ->
|
||||
instr.labelSymbol?.let {
|
||||
if(instr.opcode in OpcodesThatBranch) {
|
||||
replaceLabels.forEach { (from, to) ->
|
||||
if (it == from) {
|
||||
chunk.instructions[idx] = instr.copy(labelSymbol = to)
|
||||
}
|
||||
else {
|
||||
val actualPrefix = "$from."
|
||||
if (it.startsWith(actualPrefix))
|
||||
chunk.instructions[idx] = instr.copy(labelSymbol = "$to.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun joinChunks(sub: IRSubroutine) {
|
||||
|
@ -154,8 +179,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
chunks += sub.chunks[0]
|
||||
for(ix in 1 until sub.chunks.size) {
|
||||
val lastChunk = chunks.last()
|
||||
val candidate = sub.chunks[ix]
|
||||
when(candidate) {
|
||||
when(val candidate = sub.chunks[ix]) {
|
||||
is IRCodeChunk -> {
|
||||
if(mayJoinCodeChunks(lastChunk, candidate)) {
|
||||
lastChunk.instructions += candidate.instructions
|
||||
|
@ -296,21 +320,46 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
|
||||
val next = indexedInstructions[idx+1].value
|
||||
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
|
||||
if(previous.reg1==ins.reg1) {
|
||||
if (previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
|
||||
val next = indexedInstructions[idx + 1].value
|
||||
if (next.opcode !in OpcodesThatDependOnCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 {
|
||||
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||
var changed = false
|
||||
|
@ -382,15 +431,31 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.forEach { (idx, ins) ->
|
||||
if(ins.opcode==Opcode.STOREM) {
|
||||
val prev = indexedInstructions[idx-1].value
|
||||
if(prev.opcode==Opcode.LOADM) {
|
||||
// loadm.X rX,something | storem.X rX,something ?? -> get rid of the store.
|
||||
if(ins.labelSymbol!=null && ins.labelSymbol==prev.labelSymbol && ins.labelSymbolOffset==prev.labelSymbolOffset) {
|
||||
changed=true
|
||||
chunk.instructions.removeAt(idx)
|
||||
}
|
||||
else if(ins.address!=null && ins.address==prev.address) {
|
||||
changed=true
|
||||
chunk.instructions.removeAt(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
|
||||
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
|
||||
// TODO: detect multiple float ffrom/fto to the same target, only keep first
|
||||
// TODO: detect multiple sequential rnd with same reg1, only keep one
|
||||
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
|
||||
// TODO: detect multiple same ands, ors; only keep first
|
||||
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
|
||||
/*
|
||||
Possible other optimizations:
|
||||
// detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
|
||||
// detect multiple stores to the same target, only keep first (if target is not I/O memory)
|
||||
// detect multiple float ffrom/fto to the same target, only keep first
|
||||
// detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
|
||||
// detect multiple same ands, ors; only keep first
|
||||
// detect multiple registers being assigned the same value (and not changed) - use only 1 of them (hard!)
|
||||
// ...
|
||||
*/
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.intermediate.IRProgram
|
||||
|
||||
|
||||
class IRRegisterOptimizer(private val irProg: IRProgram) {
|
||||
fun optimize() {
|
||||
// reuseRegisters()
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: this register re-use renumbering isn't going to work like this,
|
||||
because subroutines will be clobbering the registers that the subroutine
|
||||
which is calling them might be using...
|
||||
|
||||
|
||||
private fun reuseRegisters() {
|
||||
|
||||
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
|
||||
regnum: Int,
|
||||
dt: IRDataType,
|
||||
chunk: IRCodeChunkBase) {
|
||||
val key = regnum to dt
|
||||
val chunks = usage[key] ?: mutableSetOf()
|
||||
chunks.add(chunk)
|
||||
usage[key] = chunks
|
||||
}
|
||||
|
||||
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
|
||||
|
||||
irProg.foreachCodeChunk { chunk ->
|
||||
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
|
||||
types.forEach { dt ->
|
||||
addToUsage(usage, regNum, dt, chunk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val registerReplacements = usage.asSequence()
|
||||
.filter { it.value.size==1 }
|
||||
.map { it.key to it.value.iterator().next() }
|
||||
.groupBy({ it.second }, {it.first})
|
||||
.asSequence()
|
||||
.associate { (chunk, registers) ->
|
||||
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
|
||||
}
|
||||
|
||||
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
|
||||
}
|
||||
|
||||
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
|
||||
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
|
||||
val replacementsInt = rI.associate { it.key.first to it.value }
|
||||
val replacementsFloat = rF.associate { it.key.first to it.value }
|
||||
|
||||
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
|
||||
if(fcallArgs==null)
|
||||
return null
|
||||
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
|
||||
fcallArgs.arguments.map {
|
||||
FunctionCallArgs.ArgumentSpec(
|
||||
it.name,
|
||||
it.address,
|
||||
FunctionCallArgs.RegSpec(
|
||||
it.reg.dt,
|
||||
if(it.reg.dt==IRDataType.FLOAT)
|
||||
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
|
||||
else
|
||||
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
|
||||
it.reg.cpuRegister
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
val rt = fcallArgs.returns
|
||||
val returns = if(rt==null) null else {
|
||||
FunctionCallArgs.RegSpec(
|
||||
rt.dt,
|
||||
if(rt.dt==IRDataType.FLOAT)
|
||||
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
|
||||
else
|
||||
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
|
||||
rt.cpuRegister
|
||||
)
|
||||
}
|
||||
return FunctionCallArgs(args, returns)
|
||||
}
|
||||
|
||||
fun replaceRegs(instruction: IRInstruction): IRInstruction {
|
||||
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
|
||||
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
|
||||
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
|
||||
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
|
||||
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
|
||||
}
|
||||
val newInstructions = chunk.instructions.map {
|
||||
replaceRegs(it)
|
||||
}
|
||||
chunk.instructions.clear()
|
||||
chunk.instructions.addAll(newInstructions)
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
|
@ -37,9 +36,9 @@ class IRUnusedCodeRemover(
|
|||
}
|
||||
}
|
||||
|
||||
irprog.st.allVariables().forEach { stVar->
|
||||
val initValue = stVar.onetimeInitializationArrayValue
|
||||
if(initValue!=null && !initValue.isEmpty()) {
|
||||
irprog.st.allVariables().forEach { variable ->
|
||||
val initValue = variable.onetimeInitializationArrayValue
|
||||
if(!initValue.isNullOrEmpty()) {
|
||||
if(initValue.any {
|
||||
it.addressOfSymbol?.startsWith(blockLabel)==true
|
||||
})
|
||||
|
@ -61,8 +60,8 @@ class IRUnusedCodeRemover(
|
|||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
if(!block.options.ignoreUnused) {
|
||||
errors.info("unused subroutine '${sub.label}'", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
irprog.st.removeTree(sub.label)
|
||||
|
@ -82,8 +81,8 @@ class IRUnusedCodeRemover(
|
|||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
|
||||
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
|
||||
if(!block.options.ignoreUnused) {
|
||||
errors.info("unused subroutine '${sub.label}'", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
irprog.st.removeTree(sub.label)
|
||||
|
@ -103,7 +102,7 @@ class IRUnusedCodeRemover(
|
|||
// check if asmsub is called from another asmsub
|
||||
irprog.blocks.asSequence().forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
|
||||
if (block.forceOutput || block.library)
|
||||
if (block.options.forceOutput || block.library)
|
||||
linkedAsmSubs += sub
|
||||
if (sub.asmChunk.isNotEmpty()) {
|
||||
allSubs.forEach { (label, asmsub) ->
|
||||
|
@ -149,6 +148,7 @@ class IRUnusedCodeRemover(
|
|||
val entrypointSub = irprog.blocks.single { it.label=="main" }
|
||||
.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||
reachable.add(irprog.globalInits)
|
||||
|
||||
// all chunks referenced in array initializer values are also 'reachable':
|
||||
irprog.st.allVariables()
|
||||
|
@ -168,7 +168,13 @@ class IRUnusedCodeRemover(
|
|||
it.next?.let { next -> new += next }
|
||||
it.instructions.forEach { instr ->
|
||||
if (instr.branchTarget == null)
|
||||
instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
|
||||
instr.labelSymbol?.let { label ->
|
||||
val chunk = allLabeledChunks[label] ?: allLabeledChunks[label.substringBeforeLast('.')]
|
||||
if(chunk!=null)
|
||||
new+=chunk
|
||||
else
|
||||
allLabeledChunks[label]?.let { new += it }
|
||||
}
|
||||
else
|
||||
new += instr.branchTarget!!
|
||||
}
|
||||
|
@ -215,6 +221,17 @@ class IRUnusedCodeRemover(
|
|||
linkedChunks += chunk
|
||||
}
|
||||
|
||||
// make sure that chunks that are only used as a prefix of a label, are also marked as linked
|
||||
linkedChunks.toList().forEach { chunk ->
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null) {
|
||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linkedChunks.add(irprog.globalInits)
|
||||
return removeUnlinkedChunks(linkedChunks)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.ast.*
|
||||
|
||||
|
||||
internal fun makeAllNodenamesScoped(program: PtProgram) {
|
||||
val renames = mutableListOf<Pair<PtNamedNode, String>>()
|
||||
fun recurse(node: PtNode) {
|
||||
node.children.forEach {
|
||||
if(it is PtNamedNode)
|
||||
renames.add(it to it.scopedName)
|
||||
recurse(it)
|
||||
}
|
||||
}
|
||||
recurse(program)
|
||||
renames.forEach { it.first.name = it.second }
|
||||
}
|
||||
|
||||
internal fun moveAllNestedSubroutinesToBlockScope(program: PtProgram) {
|
||||
val movedSubs = mutableListOf<Pair<PtBlock, PtSub>>()
|
||||
val removedSubs = mutableListOf<Pair<PtSub, PtSub>>()
|
||||
|
||||
fun moveToBlock(block: PtBlock, parent: PtSub, asmsub: PtAsmSub) {
|
||||
block.add(asmsub)
|
||||
parent.children.remove(asmsub)
|
||||
}
|
||||
|
||||
fun moveToBlock(block: PtBlock, parent: PtSub, sub: PtSub) {
|
||||
sub.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, sub, subsub) }
|
||||
sub.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, sub, asmsubsub) }
|
||||
movedSubs += Pair(block, sub)
|
||||
removedSubs += Pair(parent, sub)
|
||||
}
|
||||
|
||||
program.allBlocks().forEach { block ->
|
||||
block.children.toList().forEach {
|
||||
if (it is PtSub) {
|
||||
// Only regular subroutines can have nested subroutines.
|
||||
it.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, it, subsub) }
|
||||
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, it, asmsubsub) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removedSubs.forEach { (parent, sub) -> parent.children.remove(sub) }
|
||||
movedSubs.forEach { (block, sub) -> block.add(sub) }
|
||||
}
|
|
@ -3,7 +3,7 @@ import prog8.code.core.*
|
|||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
DataType.FLOAT -> 5
|
||||
else -> 2
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ internal object DummyMemsizer : IMemSizer {
|
|||
}
|
||||
|
||||
internal object DummyStringEncoder : IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -25,11 +27,11 @@ internal object DummyStringEncoder : IStringEncoding {
|
|||
}
|
||||
}
|
||||
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||
IErrorReporter {
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
|
||||
|
||||
val errors = mutableListOf<String>()
|
||||
val warnings = mutableListOf<String>()
|
||||
val infos = mutableListOf<String>()
|
||||
|
||||
override fun err(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
|
@ -43,17 +45,25 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
warnings.add(text)
|
||||
}
|
||||
|
||||
override fun info(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
if(text !in infos)
|
||||
infos.add(text)
|
||||
}
|
||||
|
||||
override fun undefined(symbol: List<String>, position: Position) {
|
||||
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||
}
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||
if(throwExceptionAtReportIfErrors)
|
||||
finalizeNumErrors(errors.size, warnings.size)
|
||||
finalizeNumErrors(errors.size, warnings.size, infos.size)
|
||||
if(!keepMessagesAfterReporting) {
|
||||
clear()
|
||||
}
|
||||
|
@ -62,5 +72,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
fun clear() {
|
||||
errors.clear()
|
||||
warnings.clear()
|
||||
infos.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import prog8.intermediate.*
|
|||
class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
require(chunks.first().label=="main.start")
|
||||
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
|
@ -24,7 +24,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
)
|
||||
val prog = IRProgram("test", IRSymbolTable(null), options, target)
|
||||
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
||||
prog.addBlock(block)
|
||||
prog.linkChunks()
|
||||
prog.validate()
|
||||
|
@ -53,9 +53,9 @@ class TestIRPeepholeOpt: FunSpec({
|
|||
|
||||
test("remove jmp to label below") {
|
||||
val c1 = IRCodeChunk("main.start", null)
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||
val c2 = IRCodeChunk("label", null)
|
||||
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label
|
||||
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2")
|
||||
c2 += IRInstruction(Opcode.NOP) // removed
|
||||
val c3 = IRCodeChunk("label2", null)
|
||||
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
|
||||
|
@ -67,15 +67,13 @@ class TestIRPeepholeOpt: FunSpec({
|
|||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 4
|
||||
irProg.chunks().size shouldBe 3
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label"
|
||||
irProg.chunks()[2].label shouldBe "label2"
|
||||
irProg.chunks()[3].label shouldBe "label3"
|
||||
irProg.chunks()[1].label shouldBe "label2"
|
||||
irProg.chunks()[2].label shouldBe "label3"
|
||||
irProg.chunks()[0].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe true
|
||||
irProg.chunks()[2].isEmpty() shouldBe false
|
||||
irProg.chunks()[3].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe false
|
||||
irProg.chunks()[2].isEmpty() shouldBe true
|
||||
val instr = irProg.chunks().flatMap { it.instructions }
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.JUMP
|
||||
|
|
|
@ -42,7 +42,7 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
|
@ -50,7 +50,7 @@ class TestVmCodeGen: FunSpec({
|
|||
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(Position.DUMMY).also {
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
|
||||
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||
|
@ -65,7 +65,7 @@ class TestVmCodeGen: FunSpec({
|
|||
sub.add(assign)
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
|
@ -73,7 +73,7 @@ class TestVmCodeGen: FunSpec({
|
|||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
|
@ -81,7 +81,7 @@ class TestVmCodeGen: FunSpec({
|
|||
sub.add(numberAssign)
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
|
@ -92,7 +92,7 @@ class TestVmCodeGen: FunSpec({
|
|||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
|
@ -121,11 +121,11 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -133,7 +133,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -141,7 +141,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -149,7 +149,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -184,11 +184,11 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -196,7 +196,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -204,7 +204,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -212,7 +212,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -243,23 +243,23 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup())
|
||||
sub.add(if2)
|
||||
block.add(sub)
|
||||
|
@ -290,11 +290,11 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -302,7 +302,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -310,7 +310,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -318,7 +318,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -353,11 +353,11 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -365,7 +365,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -373,7 +373,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -381,7 +381,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -412,23 +412,23 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup())
|
||||
sub.add(if2)
|
||||
block.add(sub)
|
||||
|
@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
|
|||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(romsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
|
|
|
@ -6,9 +6,8 @@ plugins {
|
|||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -27,6 +26,7 @@ dependencies {
|
|||
implementation project(':codeCore')
|
||||
implementation project(':compilerAst')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,17 +1,20 @@
|
|||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.IntegerDatatypesWithBoolean
|
||||
import prog8.code.core.Position
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
class ConstExprEvaluator {
|
||||
|
||||
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): Expression {
|
||||
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): NumericLiteral {
|
||||
try {
|
||||
return when(operator) {
|
||||
"+" -> plus(left, right)
|
||||
|
@ -19,9 +22,9 @@ class ConstExprEvaluator {
|
|||
"*" -> multiply(left, right)
|
||||
"/" -> divide(left, right)
|
||||
"%" -> remainder(left, right)
|
||||
"&" -> bitwiseand(left, right)
|
||||
"|" -> bitwiseor(left, right)
|
||||
"^" -> bitwisexor(left, right)
|
||||
"&" -> bitwiseAnd(left, right)
|
||||
"|" -> bitwiseOr(left, right)
|
||||
"^" -> bitwiseXor(left, right)
|
||||
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
||||
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
||||
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
||||
|
@ -30,6 +33,9 @@ class ConstExprEvaluator {
|
|||
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
|
||||
"<<" -> shiftedleft(left, right)
|
||||
">>" -> shiftedright(left, right)
|
||||
"and" -> logicalAnd(left, right)
|
||||
"or" -> logicalOr(left, right)
|
||||
"xor" -> logicalXor(left, right)
|
||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||
}
|
||||
} catch (ax: FatalAstException) {
|
||||
|
@ -37,7 +43,7 @@ class ConstExprEvaluator {
|
|||
}
|
||||
}
|
||||
|
||||
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): Expression {
|
||||
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||
val result =
|
||||
|
@ -48,52 +54,81 @@ class ConstExprEvaluator {
|
|||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||
}
|
||||
|
||||
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): Expression {
|
||||
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left << $amount", left.position)
|
||||
val result = left.number.toInt().shl(amount.number.toInt())
|
||||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||
// when(left.type) {
|
||||
// DataType.BOOL -> result = result and 1
|
||||
// DataType.UBYTE -> result = result and 255
|
||||
// DataType.BYTE -> result = result.toByte().toInt()
|
||||
// DataType.UWORD -> result = result and 65535
|
||||
// DataType.WORD -> result = result.toShort().toInt()
|
||||
// else -> { /* keep as it is */ }
|
||||
// }
|
||||
return NumericLiteral.optimalNumeric(result.toDouble(), left.position)
|
||||
}
|
||||
|
||||
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseand(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
}
|
||||
|
||||
private fun logicalAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue and right.asBooleanValue, left.position)
|
||||
|
||||
private fun logicalOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue or right.asBooleanValue, left.position)
|
||||
|
||||
private fun logicalXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue xor right.asBooleanValue, left.position)
|
||||
|
||||
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
|
@ -206,4 +241,154 @@ class ConstExprEvaluator {
|
|||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
fun evaluate(call: FunctionCallExpression, program: Program): NumericLiteral? {
|
||||
if(call.target.nameInSource.size!=2)
|
||||
return null // likely a builtin function, or user function, these get evaluated elsewhere
|
||||
val constArgs = call.args.mapNotNull { it.constValue(program) }
|
||||
if(constArgs.size!=call.args.size)
|
||||
return null
|
||||
|
||||
return when(call.target.nameInSource[0]) {
|
||||
"math" -> evalMath(call, constArgs)
|
||||
"floats" -> evalFloats(call, constArgs)
|
||||
"string" -> evalString(call, constArgs)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalFloats(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
val result= when(func.target.nameInSource[1]) {
|
||||
"pow" -> args[0].number.pow(args[1].number)
|
||||
"sin" -> sin(args[0].number)
|
||||
"cos" -> cos(args[0].number)
|
||||
"tan" -> tan(args[0].number)
|
||||
"atan" -> atan(args[0].number)
|
||||
"ln" -> ln(args[0].number)
|
||||
"log2" -> log2(args[0].number)
|
||||
"rad" -> args[0].number/360.0 * 2 * PI
|
||||
"deg" -> args[0].number/ 2 / PI * 360.0
|
||||
"round" -> round(args[0].number)
|
||||
"floor" -> floor(args[0].number)
|
||||
"ceil" -> ceil(args[0].number)
|
||||
"minf", "min" -> min(args[0].number, args[1].number)
|
||||
"maxf", "max" -> max(args[0].number, args[1].number)
|
||||
"clampf", "clamp" -> {
|
||||
var value = args[0].number
|
||||
val minimum = args[1].number
|
||||
val maximum = args[2].number
|
||||
if(value<minimum)
|
||||
value=minimum
|
||||
if(value<maximum)
|
||||
value
|
||||
else
|
||||
maximum
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if(result==null)
|
||||
null
|
||||
else
|
||||
NumericLiteral(DataType.FLOAT, result, func.position)
|
||||
}
|
||||
|
||||
private fun evalMath(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
return when(func.target.nameInSource[1]) {
|
||||
"sin8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * sin(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"cos8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * cos(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"sin8" -> {
|
||||
val value = truncate(127.0 * sin(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"cos8" -> {
|
||||
val value = truncate(127.0 * cos(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"sinr8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * sin(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"cosr8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * cos(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"sinr8" -> {
|
||||
val value = truncate(127.0 * sin(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"cosr8" -> {
|
||||
val value = truncate(127.0 * cos(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"log2" -> {
|
||||
val value = truncate(log2(args.single().number))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"log2w" -> {
|
||||
val value = truncate(log2(args.single().number))
|
||||
NumericLiteral(DataType.UWORD, value, func.position)
|
||||
}
|
||||
"atan2" -> {
|
||||
val x1f = args[0].number
|
||||
val y1f = args[1].number
|
||||
val x2f = args[2].number
|
||||
val y2f = args[3].number
|
||||
var radians = atan2(y2f-y1f, x2f-x1f)
|
||||
if(radians<0)
|
||||
radians+=2*PI
|
||||
NumericLiteral(DataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
|
||||
}
|
||||
"diff" -> {
|
||||
val n1 = args[0].number
|
||||
val n2 = args[1].number
|
||||
val value = if(n1>n2) n1-n2 else n2-n1
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"diffw" -> {
|
||||
val n1 = args[0].number
|
||||
val n2 = args[1].number
|
||||
val value = if(n1>n2) n1-n2 else n2-n1
|
||||
NumericLiteral(DataType.UWORD, value, func.position)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalString(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
return when(func.target.nameInSource[1]) {
|
||||
"isdigit" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 48..57, func.position)
|
||||
}
|
||||
"isupper" -> {
|
||||
// shifted petscii has 2 ranges that contain the upper case letters... 97-122 and 193-218
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 97..122 || char in 193..218, func.position)
|
||||
}
|
||||
"islower" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 65..90, func.position)
|
||||
}
|
||||
"isletter" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 65..90 || char in 97..122 || char in 193..218, func.position)
|
||||
}
|
||||
"isspace" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in arrayOf(32, 13, 9, 10, 141, 160), func.position)
|
||||
}
|
||||
"isprint" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 32..127 || char>=160, func.position)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,25 @@ package prog8.optimizer
|
|||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.RepeatLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclType
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
class ConstantFoldingOptimizer(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
private val evaluator = ConstExprEvaluator()
|
||||
|
||||
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
val constAddr = addressOf.constValue(program) ?: return noModifications
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, constAddr, parent))
|
||||
}
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
|
@ -28,6 +32,19 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
|
||||
if(parent is Assignment) {
|
||||
val iDt = parent.target.inferType(program)
|
||||
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
|
||||
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED), true)
|
||||
if(casted.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, casted.valueOrZero(), parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||
val result = containment.constValue(program)
|
||||
if(result!=null)
|
||||
|
@ -80,6 +97,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) {
|
||||
// mutiply a string.
|
||||
val part = expr.left as StringLiteral
|
||||
if(part.value.isEmpty())
|
||||
errors.warn("resulting string has length zero", part.position)
|
||||
val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newStr, parent))
|
||||
}
|
||||
|
@ -135,9 +154,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
}
|
||||
}
|
||||
|
||||
val evaluator = ConstExprEvaluator()
|
||||
|
||||
// const fold when both operands are a const
|
||||
// const fold when both operands are a const.
|
||||
// if in a chained comparison, that one has to be desugared first though.
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||
|
@ -185,10 +203,12 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
} else if (leftBinExpr.operator=="/") {
|
||||
// (X / C2) * rightConst --> X * (rightConst/C2)
|
||||
val constants = BinaryExpression(rightconst, "/", c2, c2.position)
|
||||
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
if(expr.inferType(program).istype(DataType.FLOAT)) {
|
||||
// (X / C2) * rightConst --> X * (rightConst/C2) only valid for floating point
|
||||
val constants = BinaryExpression(rightconst, "/", c2, c2.position)
|
||||
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +216,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
val c2 = leftBinExpr.right.constValue(program)
|
||||
if(c2!=null && leftBinExpr.operator=="/") {
|
||||
// (X / C1) / C2 --> X / (C1*C2)
|
||||
// NOTE: do not optimize (X * C1) / C2 --> X * (C1/C2) because this causes precision loss on integers
|
||||
// NOTE: do not optimize (X * C1) / C2 for integers, --> X * (C1/C2) because this causes precision loss on integers
|
||||
val constants = BinaryExpression(c2, "*", rightconst, c2.position)
|
||||
val newExpr = BinaryExpression(leftBinExpr.left, "/", constants, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
|
@ -228,51 +248,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
}
|
||||
else if(leftBinExpr.operator=="*" && rightBinExpr.operator=="*"){
|
||||
if (c1 != null && c2 != null && c1==c2) {
|
||||
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C
|
||||
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
|
||||
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
|
||||
val dt = expr.left.inferType(program)
|
||||
if(dt.isBytes && rightconst.number>=8) {
|
||||
if(dt.istype(DataType.UBYTE)) {
|
||||
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
|
||||
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isWords && rightconst.number>=16) {
|
||||
if(dt.istype(DataType.UWORD)) {
|
||||
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
|
||||
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C (only if types of X and Y are the same!)
|
||||
val xDt = leftBinExpr.left.inferType(program)
|
||||
val yDt = rightBinExpr.left.inferType(program)
|
||||
if(xDt==yDt) {
|
||||
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
|
||||
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -312,8 +294,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
val constvalue = functionCallExpr.constValue(program)
|
||||
return if(constvalue!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
|
||||
else
|
||||
noModifications
|
||||
else {
|
||||
val const2 = evaluator.evaluate(functionCallExpr, program)
|
||||
return if(const2!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCallExpr, const2, parent))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
}
|
||||
|
||||
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
|
@ -326,14 +313,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
|
||||
val fromCast = rangeFrom.cast(targetDt)
|
||||
val toCast = rangeTo.cast(targetDt)
|
||||
val fromCast = rangeFrom.cast(targetDt, true)
|
||||
val toCast = rangeTo.cast(targetDt, true)
|
||||
if(!fromCast.isValid || !toCast.isValid)
|
||||
return null
|
||||
|
||||
val newStep =
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
val stepCast = stepLiteral.cast(targetDt, true)
|
||||
if(stepCast.isValid)
|
||||
stepCast.valueOrZero()
|
||||
else
|
||||
|
@ -387,7 +374,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
||||
else -> { /* nothing for floats, these are not allowed in for loops and will give an error elsewhere */ }
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
@ -399,7 +386,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
val valueDt = numval.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
val cast = numval.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
|
@ -418,6 +405,11 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
val constValue = typecast.constValue(program) ?: return noModifications
|
||||
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
|
||||
}
|
||||
|
||||
private class ShuffleOperands(val expr: BinaryExpression,
|
||||
val exprOperator: String?,
|
||||
val subExpr: BinaryExpression,
|
||||
|
@ -443,7 +435,6 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
subrightIsConst: Boolean): IAstModification?
|
||||
{
|
||||
// NOTE: THESE REORDERINGS ARE ONLY VALID FOR FLOATING POINT CONSTANTS
|
||||
// TODO: this implements only a small set of possible reorderings at this time, we could perhaps add more
|
||||
|
||||
if(expr.operator==subExpr.operator) {
|
||||
// both operators are the same.
|
||||
|
@ -527,7 +518,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="*" && subExpr.operator=="/") {
|
||||
else if(expr.operator=="*" && subExpr.operator=="/" && subExpr.inferType(program).istype(DataType.FLOAT)) {
|
||||
// division optimizations only valid for floats
|
||||
if(leftIsConst) {
|
||||
val change = if(subleftIsConst) {
|
||||
// C1*(C2/V) -> (C1*C2)/V
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
|
@ -9,10 +11,17 @@ import prog8.ast.statements.*
|
|||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
// Fix up the literal value's type to match that of the vardecl
|
||||
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
class VarConstantValueTypeAdjuster(
|
||||
private val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
private val callGraph by lazy { CallGraph(program) }
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
|
@ -25,10 +34,11 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
|||
&& declConstValue.type != decl.datatype) {
|
||||
// avoid silent float roundings
|
||||
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
|
||||
errors.err("refused rounding of float to avoid loss of precision", decl.value!!.position)
|
||||
} else {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
} else if(decl.datatype!=DataType.BOOL) {
|
||||
// cast the numeric literal to the appropriate datatype of the variable if it's not boolean
|
||||
declConstValue.linkParents(decl)
|
||||
val cast = declConstValue.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
}
|
||||
|
@ -37,6 +47,74 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
|||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
// replace variables by constants, if possible
|
||||
if(options.optimize) {
|
||||
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
|
||||
return noModifications
|
||||
if (decl.value != null && decl.value!!.constValue(program) == null)
|
||||
return noModifications
|
||||
val usages = callGraph.usages(decl)
|
||||
val (writes, reads) = usages
|
||||
.partition {
|
||||
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|
||||
|| it.parent is AssignTarget
|
||||
|| it.parent is ForLoop
|
||||
|| it.parent is AddressOf
|
||||
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
|
||||
}
|
||||
val singleAssignment =
|
||||
writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
|
||||
if (singleAssignment == null) {
|
||||
if (writes.isEmpty()) {
|
||||
if(reads.isEmpty()) {
|
||||
// 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)
|
||||
if (declValue != null) {
|
||||
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
|
||||
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.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
decl.value = null
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
|
||||
if(reads.isEmpty()) {
|
||||
// variable is never used AT ALL so we just remove it altogether, including the single assignment
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
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
|
||||
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.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent),
|
||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
/*
|
||||
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
|
||||
if(writes.size==2) {
|
||||
val firstAssignment = writes[0].parent as? Assignment
|
||||
val secondAssignment = writes[1].parent as? Assignment
|
||||
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
|
||||
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -193,7 +271,19 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
|||
// Replace all constant identifiers with their actual value,
|
||||
// and the array var initializer values and sizes.
|
||||
// This is needed because further constant optimizations depend on those.
|
||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
internal class ConstantIdentifierReplacer(
|
||||
private val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
val constValue = addressOf.constValue(program)
|
||||
if(constValue!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, constValue, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
// replace identifiers that refer to const value, with the value itself
|
||||
|
@ -207,7 +297,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
return noModifications
|
||||
|
||||
val dt = identifier.inferType(program)
|
||||
if(!dt.isKnown || !dt.isNumeric)
|
||||
if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
|
||||
return noModifications
|
||||
|
||||
try {
|
||||
|
@ -220,23 +310,27 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
}
|
||||
return when (cval.type) {
|
||||
in NumericDatatypes -> listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
when (cval.type) {
|
||||
in NumericDatatypesWithBoolean -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
in PassByReferenceDatatypes -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> noModifications
|
||||
else -> return noModifications
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
|
@ -244,14 +338,14 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
}
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true) {
|
||||
errors.err("recursive var declaration", decl.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(decl.isArray && decl.type==VarDeclType.MEMORY) {
|
||||
if(decl.isArray && decl.type==VarDeclType.MEMORY && decl.value !is IdentifierReference) {
|
||||
val memaddr = decl.value?.constValue(program)
|
||||
if(memaddr!=null && memaddr !== decl.value) {
|
||||
return listOf(IAstModification.SetExpression(
|
||||
|
@ -280,7 +374,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
DataType.FLOAT -> {
|
||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||
val litval = decl.value as? NumericLiteral
|
||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||
if (litval!=null && litval.type in IntegerDatatypesWithBoolean) {
|
||||
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
|
@ -381,12 +475,11 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
// arraysize initializer is a single int, and we know the array size.
|
||||
val fillvalue = numericLv.number
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
if (fillvalue < options.compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > options.compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", numericLv.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
}
|
||||
|
@ -396,9 +489,13 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>()
|
||||
// arraysize initializer is a single value, and we know the array size.
|
||||
if(numericLv.type!=DataType.BOOL) {
|
||||
if(options.strictBool || numericLv.type !in ByteDatatypes)
|
||||
errors.err("initializer value is not a boolean", numericLv.position)
|
||||
return null
|
||||
}
|
||||
val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,19 @@
|
|||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.IfElse
|
||||
import prog8.ast.statements.Jump
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
|
||||
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has?
|
||||
|
||||
class ExpressionSimplifier(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
class ExpressionSimplifier(private val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
|
||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
|
||||
|
@ -32,7 +23,7 @@ class ExpressionSimplifier(private val program: Program,
|
|||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
|
@ -66,30 +57,36 @@ class ExpressionSimplifier(private val program: Program,
|
|||
)
|
||||
}
|
||||
if(elsepart.statements.singleOrNull() is Jump) {
|
||||
val invertedCondition = invertCondition(ifElse.condition)
|
||||
if(invertedCondition!=null) {
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if(compTarget.name!=VMTarget.NAME) {
|
||||
val booleanCondition = ifElse.condition as? BinaryExpression
|
||||
if(booleanCondition!=null && booleanCondition.operator=="&") {
|
||||
// special optimization of WORD & $ff00 -> just and the msb of WORD with $ff
|
||||
val rightNum = booleanCondition.right as? NumericLiteral
|
||||
if(rightNum!=null && rightNum.type==DataType.UWORD && (rightNum.number.toInt() and 0x00ff)==0) {
|
||||
val booleanCondition = ifElse.condition as? BinaryExpression
|
||||
if(booleanCondition!=null && booleanCondition.operator=="&") {
|
||||
val rightNum = booleanCondition.right as? NumericLiteral
|
||||
if (rightNum!=null && rightNum.type==DataType.UWORD) {
|
||||
if ((rightNum.number.toInt() and 0x00ff) == 0) {
|
||||
// if WORD & $xx00 -> if msb(WORD) & $xx
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
|
||||
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
|
||||
}
|
||||
else if ((rightNum.number.toInt() and 0xff00) == 0) {
|
||||
// if WORD & $00ff -> if lsb(WORD) & $ff
|
||||
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, rightNum.number, booleanCondition.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(booleanCondition.left, lsb, booleanCondition),
|
||||
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,6 +94,10 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val newExpr = applyAbsorptionLaws(expr)
|
||||
if(newExpr!=null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
|
||||
val leftVal = expr.left.constValue(program)
|
||||
val rightVal = expr.right.constValue(program)
|
||||
|
||||
|
@ -105,16 +106,6 @@ class ExpressionSimplifier(private val program: Program,
|
|||
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
|
||||
// X + (-A) --> X - A
|
||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
|
@ -158,8 +149,8 @@ class ExpressionSimplifier(private val program: Program,
|
|||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
} else {
|
||||
// Y*X - X -> X*(Y - 1)
|
||||
|
@ -168,8 +159,8 @@ class ExpressionSimplifier(private val program: Program,
|
|||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yMinus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
}
|
||||
} else if (rightBinExpr?.operator == "*") {
|
||||
|
@ -180,8 +171,8 @@ class ExpressionSimplifier(private val program: Program,
|
|||
val y = determineY(x, rightBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteral.optimalInteger(1, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,31 +237,106 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
}
|
||||
|
||||
// boolvar & 1 --> boolvar
|
||||
// boolvar & 2 --> false
|
||||
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
if(rightVal?.number==1.0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
||||
// optimize boolean constant comparisons
|
||||
if(expr.operator=="==") {
|
||||
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
|
||||
val rightConstBool = rightVal?.asBooleanValue
|
||||
if(rightConstBool!=null) {
|
||||
return if(rightConstBool)
|
||||
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
else
|
||||
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expr.operator=="!=") {
|
||||
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
|
||||
val rightConstBool = rightVal?.asBooleanValue
|
||||
if(rightConstBool!=null) {
|
||||
return if (rightConstBool)
|
||||
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
else
|
||||
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt==DataType.BOOL) {
|
||||
// optimize boolean constant comparisons
|
||||
// if(expr.operator=="==" && rightVal?.number==0.0)
|
||||
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
// if(expr.operator=="!=" && rightVal?.number==1.0)
|
||||
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
if(expr.operator=="==" && rightVal?.number==1.0)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if(expr.operator=="!=" && rightVal?.number==0.0)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if(expr.operator in arrayOf("and", "or", "xor")) {
|
||||
if(leftVal!=null) {
|
||||
val result = if(leftVal.asBooleanValue) {
|
||||
when(expr.operator) {
|
||||
"and" -> expr.right
|
||||
"or" -> NumericLiteral.fromBoolean(true, expr.position)
|
||||
"xor" -> PrefixExpression("not", expr.right, expr.position)
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
} else {
|
||||
when(expr.operator) {
|
||||
"and" -> NumericLiteral.fromBoolean(false, expr.position)
|
||||
"or" -> expr.right
|
||||
"xor" -> expr.right
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
else if(rightVal!=null) {
|
||||
val result = if(rightVal.asBooleanValue) {
|
||||
when(expr.operator) {
|
||||
"and" -> expr.left
|
||||
"or" -> NumericLiteral.fromBoolean(true, expr.position)
|
||||
"xor" -> PrefixExpression("not", expr.left, expr.position)
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
} else {
|
||||
when(expr.operator) {
|
||||
"and" -> NumericLiteral.fromBoolean(false, expr.position)
|
||||
"or" -> expr.left
|
||||
"xor" -> expr.left
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
}
|
||||
|
||||
// simplify when a term is constant and directly determines the outcome
|
||||
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
|
||||
val newExpr: Expression? = when (expr.operator) {
|
||||
val newExpr2 = when (expr.operator) {
|
||||
"|" -> {
|
||||
when {
|
||||
leftVal?.number==0.0 -> expr.right
|
||||
|
@ -315,33 +381,132 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
|
||||
if(rightVal!=null && leftDt==DataType.BOOL) {
|
||||
// see if we can replace comparison against true/1 with simpler comparison against zero
|
||||
if (expr.operator == "==") {
|
||||
if (rightVal.number == 1.0) {
|
||||
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||
return listOf(
|
||||
IAstModification.SetExpression({expr.operator="!="}, expr, parent),
|
||||
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (expr.operator == "!=") {
|
||||
if (rightVal.number == 1.0) {
|
||||
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||
return listOf(
|
||||
IAstModification.SetExpression({expr.operator="=="}, expr, parent),
|
||||
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||
)
|
||||
// boolean compare against a number -> just keep the boolean, no compare
|
||||
if(expr.operator=="==" || expr.operator=="!=") {
|
||||
val test = if (expr.operator == "==") rightVal.asBooleanValue else !rightVal.asBooleanValue
|
||||
return if (test) {
|
||||
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
} else {
|
||||
listOf(IAstModification.ReplaceNode(expr, invertCondition(expr.left, program), parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(newExpr != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
if(newExpr2 != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr2, parent))
|
||||
|
||||
if (rightVal!=null && (expr.operator == "==" || expr.operator == "!=")) {
|
||||
val bitwise = expr.left as? BinaryExpression
|
||||
if(bitwise!=null && bitwise.operator=="&" && bitwise.inferType(program).isWords) {
|
||||
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
|
||||
if (andNum!=null) {
|
||||
if ((andNum and 0x00ff) == 0) {
|
||||
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
|
||||
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
|
||||
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
|
||||
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
|
||||
)
|
||||
}
|
||||
else if((andNum and 0xff00) == 0) {
|
||||
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
|
||||
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, andNum.toDouble(), bitwise.right.position)
|
||||
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
|
||||
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
|
||||
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator=="not") {
|
||||
// not X <compare> Y -> X <invertedcompare> Y
|
||||
val binExpr = expr.expression as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
val invertedOperator = invertedComparisonOperator(binExpr.operator)
|
||||
if(invertedOperator!=null) {
|
||||
val inverted = BinaryExpression(binExpr.left, invertedOperator, binExpr.right, binExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, inverted, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
// NOTE: only when the terms are not function calls!!!
|
||||
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
|
||||
return null
|
||||
val rightB = expr.right as? BinaryExpression
|
||||
if(rightB!=null) {
|
||||
if(rightB.left is IFunctionCall || rightB.right is IFunctionCall)
|
||||
return null
|
||||
// absorption laws: a or (a and b) --> a, a and (a or b) --> a
|
||||
if(expr.operator=="or" && rightB.operator=="and") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && rightB.operator=="or") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="or" && rightB.operator=="or") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
// a or (a or b) -> a or b
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && rightB.operator=="and") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
// a and (a and b) -> a and b
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
}
|
||||
val leftB = expr.left as? BinaryExpression
|
||||
if(leftB!=null) {
|
||||
if(leftB.left is IFunctionCall || leftB.right is IFunctionCall)
|
||||
return null
|
||||
// absorption laws: (a and b) or a --> a, (a or b) and a --> a
|
||||
if(expr.operator=="or" && leftB.operator=="and") {
|
||||
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && leftB.operator=="or") {
|
||||
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="or" && leftB.operator=="or") {
|
||||
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
|
||||
// (a or b) or a -> a or b
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && leftB.operator=="and") {
|
||||
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
|
||||
// (a and b) or a -> a and b
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
|
||||
if(functionCallExpr.args.isEmpty())
|
||||
|
@ -349,10 +514,15 @@ class ExpressionSimplifier(private val program: Program,
|
|||
val arg = functionCallExpr.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value that was typecasted to word
|
||||
if (valueDt istype DataType.UBYTE) {
|
||||
// useless lsb() of ubyte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
|
||||
}
|
||||
else if (valueDt istype DataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
if(arg is IdentifierReference && arg.nameInSource.size==2
|
||||
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
|
||||
|
@ -361,10 +531,15 @@ class ExpressionSimplifier(private val program: Program,
|
|||
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
|
||||
}
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||
if (argDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg, parent))
|
||||
}
|
||||
else if (argDt istype DataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
|
||||
|
@ -405,6 +580,14 @@ class ExpressionSimplifier(private val program: Program,
|
|||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("string", "contains")) {
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
|
||||
if(target?.value is StringLiteral) {
|
||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
||||
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr as Node, contains, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
@ -540,6 +723,21 @@ class ExpressionSimplifier(private val program: Program,
|
|||
return expr.left
|
||||
}
|
||||
}
|
||||
256.0 -> {
|
||||
when(leftDt) {
|
||||
DataType.UBYTE -> return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
DataType.BYTE -> return null // is either 0 or -1 we cannot tell here
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
// just use: msb(value) as type
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt==DataType.WORD)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
in powersOfTwo -> {
|
||||
if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
|
||||
// Unsigned number divided by a power of two => shift right
|
||||
|
@ -705,8 +903,8 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
DataType.UWORD -> {
|
||||
if (amount >= 16) {
|
||||
errors.warn("shift always results in 0", expr.position)
|
||||
return NumericLiteral.optimalInteger(0, expr.position)
|
||||
errors.err("useless to shift by more than 15 bits", expr.position)
|
||||
return null
|
||||
}
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
|
@ -720,11 +918,21 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// bit-shifting a signed value shouldn't be allowed by the compiler but here we go...
|
||||
if (amount > 16) {
|
||||
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
|
||||
if (amount >= 16) {
|
||||
errors.err("useless to shift by more than 15 bits", expr.position)
|
||||
return null
|
||||
}
|
||||
else if(amount == 8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
}
|
||||
else if(amount > 8) {
|
||||
// same as above but with residual shifts. Take care to do signed shift.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
|
|
|
@ -3,17 +3,16 @@ package prog8.optimizer
|
|||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||
fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, options, errors)
|
||||
valuetypefixer.visit(this)
|
||||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||
val replacer = ConstantIdentifierReplacer(this, options, errors)
|
||||
replacer.visit(this)
|
||||
if (errors.noErrors()) {
|
||||
replacer.applyModifications()
|
||||
|
@ -22,7 +21,7 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
|
|||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this)
|
||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||
optimizer.visit(this)
|
||||
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||
optimizer.visit(this)
|
||||
|
@ -60,8 +59,8 @@ fun Program.inlineSubroutines(options: CompilationOptions): Int {
|
|||
return inliner.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarget) : Int {
|
||||
val opti = ExpressionSimplifier(this, errors, target)
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter, options: CompilationOptions) : Int {
|
||||
val opti = ExpressionSimplifier(this, options, errors)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
|
|
@ -34,85 +34,91 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
|
||||
if(!containsSubsOrVariables) {
|
||||
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))) {
|
||||
// subroutine is possible candidate to be inlined
|
||||
subroutine.inline =
|
||||
when(val stmt=subroutine.statements[0]) {
|
||||
is Return -> {
|
||||
if(stmt.value is NumericLiteral)
|
||||
true
|
||||
else if(stmt.value==null)
|
||||
true
|
||||
else if (stmt.value is IdentifierReference) {
|
||||
makeFullyScoped(stmt.value as IdentifierReference)
|
||||
true
|
||||
} else if(stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size<=1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
when (stmt.value) {
|
||||
is BuiltinFunctionCall -> {
|
||||
makeFullyScoped(stmt.value as BuiltinFunctionCall)
|
||||
true
|
||||
if(subroutine !== program.entrypoint) {
|
||||
// subroutine is possible candidate to be inlined
|
||||
subroutine.inline =
|
||||
when (val stmt = subroutine.statements[0]) {
|
||||
is Return -> {
|
||||
if (stmt.value is NumericLiteral)
|
||||
true
|
||||
else if (stmt.value == null)
|
||||
true
|
||||
else if (stmt.value is IdentifierReference) {
|
||||
makeFullyScoped(stmt.value as IdentifierReference)
|
||||
true
|
||||
} else if (stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size <= 1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
when (stmt.value) {
|
||||
is BuiltinFunctionCall -> {
|
||||
makeFullyScoped(stmt.value as BuiltinFunctionCall)
|
||||
true
|
||||
}
|
||||
|
||||
is FunctionCallExpression -> {
|
||||
makeFullyScoped(stmt.value as FunctionCallExpression)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
is FunctionCallExpression -> {
|
||||
makeFullyScoped(stmt.value as FunctionCallExpression)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
} else
|
||||
false
|
||||
}
|
||||
is Assignment -> {
|
||||
if(stmt.value.isSimple) {
|
||||
val targetInline =
|
||||
if(stmt.target.identifier!=null) {
|
||||
makeFullyScoped(stmt.target.identifier!!)
|
||||
true
|
||||
} else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
val valueInline =
|
||||
if(stmt.value is IdentifierReference) {
|
||||
makeFullyScoped(stmt.value as IdentifierReference)
|
||||
true
|
||||
} else if((stmt.value as? DirectMemoryRead)?.addressExpression is NumericLiteral || (stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference) {
|
||||
if((stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference)
|
||||
makeFullyScoped((stmt.value as? DirectMemoryRead)?.addressExpression as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
targetInline || valueInline
|
||||
} else
|
||||
false
|
||||
}
|
||||
is BuiltinFunctionCallStatement -> {
|
||||
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||
if(inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
is FunctionCallStatement -> {
|
||||
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||
if(inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
is PostIncrDecr -> {
|
||||
if(stmt.target.identifier!=null) {
|
||||
makeFullyScoped(stmt.target.identifier!!)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
|
||||
is Assignment -> {
|
||||
if (stmt.value.isSimple) {
|
||||
val targetInline =
|
||||
if (stmt.target.identifier != null) {
|
||||
makeFullyScoped(stmt.target.identifier!!)
|
||||
true
|
||||
} else if (stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||
if (stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
val valueInline =
|
||||
if (stmt.value is IdentifierReference) {
|
||||
makeFullyScoped(stmt.value as IdentifierReference)
|
||||
true
|
||||
} else if ((stmt.value as? DirectMemoryRead)?.addressExpression is NumericLiteral || (stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference) {
|
||||
if ((stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference)
|
||||
makeFullyScoped((stmt.value as? DirectMemoryRead)?.addressExpression as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
targetInline || valueInline
|
||||
} else if(stmt.target.identifier!=null && stmt.isAugmentable) {
|
||||
val binExpr = stmt.value as BinaryExpression
|
||||
if(binExpr.operator in "+-" && binExpr.right.constValue(program)?.number==1.0) {
|
||||
makeFullyScoped(stmt.target.identifier!!)
|
||||
makeFullyScoped(binExpr.left as IdentifierReference)
|
||||
true
|
||||
} else
|
||||
false
|
||||
} else
|
||||
false
|
||||
}
|
||||
|
||||
is BuiltinFunctionCallStatement -> {
|
||||
val inline =
|
||||
stmt.args.size <= 1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||
if (inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
|
||||
is FunctionCallStatement -> {
|
||||
val inline =
|
||||
stmt.args.size <= 1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||
if (inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
|
||||
is Jump -> true
|
||||
else -> false
|
||||
}
|
||||
is Jump -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(subroutine.inline && subroutine.statements.size>1) {
|
||||
|
@ -134,16 +140,20 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||
|
||||
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
if(scopedArgs.any()) {
|
||||
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
if(scopedArgs.any()) {
|
||||
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,8 +161,10 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
if(scopedArgs.any()) {
|
||||
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,8 +172,10 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
if(scopedArgs.any()) {
|
||||
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +184,8 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||
when (it) {
|
||||
is NumericLiteral -> it.copy()
|
||||
is IdentifierReference -> {
|
||||
val scoped = (it.targetStatement(program)!! as INamedStatement).scopedName
|
||||
val target = it.targetStatement(program) ?: return emptyList()
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
IdentifierReference(scoped, it.position)
|
||||
}
|
||||
else -> throw InternalCompilerException("expected only number or identifier arg, otherwise too complex")
|
||||
|
|
|
@ -5,9 +5,10 @@ import prog8.ast.expressions.*
|
|||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import kotlin.math.floor
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
class StatementOptimizer(private val program: Program,
|
||||
|
@ -45,11 +46,7 @@ class StatementOptimizer(private val program: Program,
|
|||
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val stringDecl = string.parent as VarDecl
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout, parent),
|
||||
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = options.compTarget.encodeString(string.value.take(2), string.encoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
|
@ -62,11 +59,9 @@ class StatementOptimizer(private val program: Program,
|
|||
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[1].toDouble(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val stringDecl = string.parent as VarDecl
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent),
|
||||
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -77,13 +72,18 @@ class StatementOptimizer(private val program: Program,
|
|||
}
|
||||
|
||||
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
|
||||
val constvalue = ifElse.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
errors.warn("condition is always ${constvalue.asBooleanValue}", ifElse.condition.position)
|
||||
}
|
||||
|
||||
// remove empty if statements
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isEmpty())
|
||||
return listOf(IAstModification.Remove(ifElse, parent as IStatementContainer))
|
||||
|
||||
// empty true part? switch with the else part
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
|
@ -93,35 +93,60 @@ class StatementOptimizer(private val program: Program,
|
|||
)
|
||||
}
|
||||
|
||||
val constvalue = ifElse.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only if-part
|
||||
if(!ifElse.definingModule.isLibrary)
|
||||
errors.warn("condition is always true", ifElse.condition.position)
|
||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
|
||||
} else {
|
||||
// always false -> keep only else-part
|
||||
if(!ifElse.definingModule.isLibrary)
|
||||
errors.warn("condition is always false", ifElse.condition.position)
|
||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
|
||||
}
|
||||
}
|
||||
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
if(ifElse.elsepart.isNotEmpty()) {
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
|
||||
// switch if/else around if the else is just a jump or branch
|
||||
if(ifElse.elsepart.statements.size==1) {
|
||||
val jump = ifElse.elsepart.statements[0]
|
||||
if(jump is Jump) {
|
||||
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// switch if/else around if the condition is a not
|
||||
val prefixCond = ifElse.condition as? PrefixExpression
|
||||
if(prefixCond?.operator=="not") {
|
||||
errors.info("invert conditon and swap if/else blocks", ifElse.condition.position)
|
||||
val newTruePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, prefixCond.expression, ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
if(forLoop.body.isEmpty()) {
|
||||
errors.warn("removing empty for loop", forLoop.position)
|
||||
errors.info("removing empty for loop", forLoop.position)
|
||||
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||
} else if(forLoop.body.statements.size==1) {
|
||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||
|
@ -137,7 +162,7 @@ class StatementOptimizer(private val program: Program,
|
|||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
|
@ -152,12 +177,12 @@ class StatementOptimizer(private val program: Program,
|
|||
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
else if(iterable.datatype in ArrayDatatypes) {
|
||||
else if(iterable.isArray) {
|
||||
val size = iterable.arraysize!!.constIndex()
|
||||
if(size==1) {
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
|
@ -165,7 +190,7 @@ class StatementOptimizer(private val program: Program,
|
|||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
|
@ -174,6 +199,35 @@ class StatementOptimizer(private val program: Program,
|
|||
}
|
||||
}
|
||||
|
||||
val loopvarDt = forLoop.loopVarDt(program)
|
||||
if(loopvarDt.istype(DataType.UWORD) || loopvarDt.istype(DataType.UBYTE)) {
|
||||
|
||||
fun incOrDec(inc: Boolean): Assignment {
|
||||
val pos = forLoop.position
|
||||
val loopVar = forLoop.loopVar
|
||||
val addSubOne = BinaryExpression(loopVar.copy(), if(inc) "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false)
|
||||
return Assignment(AssignTarget(loopVar.copy(), null, null, null, false, pos), addSubOne, AssignmentOrigin.USERCODE, pos)
|
||||
}
|
||||
|
||||
if (range != null && range.to.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==-1.0) {
|
||||
val fromExpr = range.from
|
||||
if(fromExpr.constValue(program)==null) {
|
||||
// FOR X = something DOWNTO 0 {...} --> X=something, DO { ... , X-- } UNTIL X=255 (or 65535 if uword)
|
||||
val pos = forLoop.position
|
||||
val checkValue = NumericLiteral(loopvarDt.getOr(DataType.UNDEFINED), if(loopvarDt.istype(DataType.UBYTE)) 255.0 else 65535.0, pos)
|
||||
val condition = BinaryExpression(forLoop.loopVar.copy(), "==", checkValue, pos)
|
||||
val decOne = incOrDec(false)
|
||||
forLoop.body.statements.add(decOne)
|
||||
val replacement = AnonymousScope(mutableListOf(
|
||||
Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, null, false, pos),
|
||||
fromExpr, AssignmentOrigin.OPTIMIZER, pos),
|
||||
UntilLoop(forLoop.body, condition, pos)
|
||||
), pos)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, replacement, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -213,7 +267,7 @@ class StatementOptimizer(private val program: Program,
|
|||
val iter = repeatLoop.iterations
|
||||
if(iter!=null) {
|
||||
if(repeatLoop.body.isEmpty()) {
|
||||
errors.warn("empty loop removed", repeatLoop.position)
|
||||
errors.info("empty loop removed", repeatLoop.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||
}
|
||||
val iterations = iter.constValue(program)?.number?.toInt()
|
||||
|
@ -317,35 +371,15 @@ class StatementOptimizer(private val program: Program,
|
|||
// assignments of the form: X = X <operator> <expr>
|
||||
// remove assignments that have no effect (such as X=X+0)
|
||||
// optimize/rewrite some other expressions
|
||||
val targetDt = targetIDt.getOr(DataType.UNDEFINED)
|
||||
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program))?.type
|
||||
when (bexpr.operator) {
|
||||
"+" -> {
|
||||
if (rightCv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
|
||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(rightCv.toInt()) {
|
||||
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if (rightCv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
|
||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(rightCv.toInt()) {
|
||||
decs.statements.add(PostIncrDecr(assignment.target.copy(), "--", assignment.position))
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
|
@ -380,6 +414,33 @@ class StatementOptimizer(private val program: Program,
|
|||
}
|
||||
}
|
||||
|
||||
// xx+=2 -> xx++ xx++
|
||||
// note: ideally this optimization should be done by the code generator, but doing it there
|
||||
// requires doing it multiple times (because lots of different things can be incremented/decremented)
|
||||
if(assignment.target.identifier!=null
|
||||
|| assignment.target.arrayindexed?.isSimple==true
|
||||
|| assignment.target.memoryAddress?.addressExpression?.isSimple==true) {
|
||||
if(assignment.value.inferType(program).isBytes && assignment.isAugmentable) {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.operator in "+-") {
|
||||
val value = binExpr.right.constValue(program)?.number?.toInt()
|
||||
if(value!=null && value==2) {
|
||||
val stmts = mutableListOf<Statement>()
|
||||
repeat(value) {
|
||||
val incrdecr = Assignment(assignment.target.copy(),
|
||||
BinaryExpression(assignment.target.toExpression(), binExpr.operator, NumericLiteral.optimalInteger(1, assignment.position), assignment.position),
|
||||
AssignmentOrigin.OPTIMIZER, assignment.position)
|
||||
stmts.add(incrdecr)
|
||||
}
|
||||
val incrdecrs = AnonymousScope(stmts, assignment.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment, incrdecrs, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -393,44 +454,6 @@ class StatementOptimizer(private val program: Program,
|
|||
|
||||
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
|
||||
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
|
||||
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
|
||||
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
|
||||
}
|
||||
|
||||
if(whenStmt.condition.inferType(program).isBool) {
|
||||
if(whenStmt.choices.all { it.values?.size==1 }) {
|
||||
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
|
||||
// it's a when statement on booleans that can just be replaced by an if or if..else.
|
||||
if (whenStmt.choices.size == 1) {
|
||||
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
|
||||
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
|
||||
} else {
|
||||
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
|
||||
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
|
||||
}
|
||||
} else if (whenStmt.choices.size == 2) {
|
||||
var trueBlock: AnonymousScope? = null
|
||||
var elseBlock: AnonymousScope? = null
|
||||
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
|
||||
trueBlock = whenStmt.choices[0].statements
|
||||
} else {
|
||||
elseBlock = whenStmt.choices[0].statements
|
||||
}
|
||||
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
|
||||
trueBlock = whenStmt.choices[1].statements
|
||||
} else {
|
||||
elseBlock = whenStmt.choices[1].statements
|
||||
}
|
||||
if(trueBlock!=null && elseBlock!=null) {
|
||||
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val constantValue = whenStmt.condition.constValue(program)?.number
|
||||
if(constantValue!=null) {
|
||||
// when condition is a constant
|
||||
|
|
|
@ -20,7 +20,12 @@ class UnusedCodeRemover(private val program: Program,
|
|||
private val compTarget: ICompilationTarget
|
||||
): AstWalker() {
|
||||
|
||||
private val callgraph = CallGraph(program)
|
||||
private lateinit var callgraph: CallGraph
|
||||
|
||||
override fun before(program: Program): Iterable<IAstModification> {
|
||||
callgraph = CallGraph(program)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(module: Module, parent: Node): Iterable<IAstModification> {
|
||||
return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module)))
|
||||
|
@ -64,16 +69,16 @@ class UnusedCodeRemover(private val program: Program,
|
|||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if(block.name != internedStringsModuleName) {
|
||||
if(block.name != internedStringsModuleName && "ignore_unused" !in block.options()) {
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||
}
|
||||
if(callgraph.unused(block)) {
|
||||
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) {
|
||||
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR} && "ignore_unused" !in block.options()) {
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
|
||||
program.removeInternedStringsFromRemovedBlock(block)
|
||||
|
@ -90,8 +95,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
|
||||
if(callgraph.unused(subroutine)) {
|
||||
if(subroutine.containsNoCodeNorVars) {
|
||||
if(!subroutine.definingModule.isLibrary)
|
||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||
if("ignore_unused" !in subroutine.definingBlock.options())
|
||||
errors.info("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||
callgraph.calledBy[subroutine]?.let {
|
||||
for(node in it)
|
||||
|
@ -99,8 +104,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||
}
|
||||
return removals
|
||||
}
|
||||
if(!subroutine.definingModule.isLibrary && !subroutine.hasBeenInlined) {
|
||||
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
if(!subroutine.hasBeenInlined && "ignore_unused" !in subroutine.definingBlock.options()) {
|
||||
errors.info("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
}
|
||||
if(!subroutine.inline) {
|
||||
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
||||
|
@ -119,8 +124,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
||||
val usages = callgraph.usages(decl)
|
||||
if (usages.isEmpty()) {
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||
}
|
||||
else {
|
||||
|
@ -131,23 +136,30 @@ class UnusedCodeRemover(private val program: Program,
|
|||
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
|
||||
if(assignment.value.isSimple) {
|
||||
// remove the vardecl
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
} else if(assignment.value is IFunctionCall) {
|
||||
// replace the unused variable's initializer function call by a void
|
||||
errors.warn("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
// but only if the vardecl immediately precedes it!
|
||||
if(singleUse.parent.parent === parent) {
|
||||
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
|
||||
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
|
||||
if(declIndex==singleUseIndex-1) {
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.warn("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||
errors.info("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,13 @@ plugins {
|
|||
id 'java'
|
||||
id 'application'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||
id 'io.kotest' version '0.3.9'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'com.peterabeles.gversion' version '1.10.2'
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
@ -37,10 +35,14 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
|
||||
testImplementation project(':codeCore')
|
||||
testImplementation project(':intermediate')
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
@ -102,7 +104,7 @@ test {
|
|||
gversion {
|
||||
srcDir = "src/" // path is relative to the sub-project by default
|
||||
classPackage = "prog8.buildversion"
|
||||
className = "BuildVersion"
|
||||
className = "Version"
|
||||
language = "kotlin"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
; Prog8 definitions for the Atari800XL
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
atari {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
&uword COLCRS = 85
|
||||
&ubyte ROWCRS = 84
|
||||
|
||||
romsub $F24A = getchar() -> ubyte @A
|
||||
romsub $F2B0 = outchar(ubyte character @ A)
|
||||
romsub $F2FD = waitkey() -> ubyte @A
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
|
||||
|
||||
asmsub init_system() {
|
||||
|
@ -93,20 +99,14 @@ sys {
|
|||
rts ; nothing to copy
|
||||
|
||||
_copyshort
|
||||
; decrease source and target pointers so we can simply index by Y
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W2+1
|
||||
+ dec P8ZP_SCRATCH_W2
|
||||
|
||||
dey
|
||||
beq +
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bne -
|
||||
+ lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
|
||||
_longcopy
|
||||
|
@ -206,9 +206,49 @@ _longcopy
|
|||
; no-op
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
asmsub save_prog8_internals() {
|
||||
%asm {{
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta save_SCRATCH_ZPB1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta save_SCRATCH_ZPREG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta save_SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta save_SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta save_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_prog8_internals() {
|
||||
%asm {{
|
||||
lda save_prog8_internals.save_SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda save_prog8_internals.save_SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda save_prog8_internals.save_SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda save_prog8_internals.save_SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda save_prog8_internals.save_SCRATCH_ZPWORD2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda save_prog8_internals.save_SCRATCH_ZPWORD2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
; TODO
|
||||
; TODO where to store A as exit code?
|
||||
%asm {{
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
|
@ -216,6 +256,22 @@ _longcopy
|
|||
}}
|
||||
}
|
||||
|
||||
asmsub exit2(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) {
|
||||
; -- immediately exit the program with result values in the A, X and Y registers.
|
||||
; TODO where to store A,X,Y as exit code?
|
||||
%asm {{
|
||||
jmp exit
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub exit3(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) {
|
||||
; -- immediately exit the program with result values in the A, X and Y registers, and the Carry flag in the status register.
|
||||
; TODO where to store A,X,Y,Carry as exit code?
|
||||
%asm {{
|
||||
jmp exit
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progend() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_end
|
||||
|
@ -223,11 +279,37 @@ _longcopy
|
|||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the Atari as well but their location in memory is different
|
||||
; TODO
|
||||
|
@ -359,4 +441,9 @@ cx16 {
|
|||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,47 +1,53 @@
|
|||
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
|
||||
; Reference: https://www.atariarchives.org/mapping/appendix12.php
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 24
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(125)
|
||||
chrout(125)
|
||||
}
|
||||
|
||||
sub cls() {
|
||||
chrout(125)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
chrout(' ')
|
||||
}
|
||||
|
||||
sub bell() {
|
||||
chrout($fd)
|
||||
}
|
||||
|
||||
const uword COLCRS = 85
|
||||
const uword ROWCRS = 84
|
||||
sub column(ubyte col) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
pokew(COLCRS, col as uword)
|
||||
atari.COLCRS = col
|
||||
}
|
||||
|
||||
sub get_column() -> ubyte {
|
||||
return peekw(COLCRS) as ubyte
|
||||
return atari.COLCRS as ubyte
|
||||
}
|
||||
|
||||
sub row(ubyte rownum) {
|
||||
; ---- set the cursor on the given row (starting with 0) on the current line
|
||||
@(ROWCRS) = rownum
|
||||
atari.ROWCRS = rownum
|
||||
}
|
||||
|
||||
sub get_row() -> ubyte {
|
||||
return @(ROWCRS)
|
||||
return atari.ROWCRS
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
|
@ -126,9 +132,6 @@ asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
|||
}
|
||||
|
||||
|
||||
romsub $F2B0 = outchar(ubyte character @ A)
|
||||
romsub $F2Fd = waitkey()
|
||||
|
||||
asmsub chrout(ubyte character @ A) {
|
||||
%asm {{
|
||||
sta _tmp_outchar+1
|
||||
|
@ -138,7 +141,7 @@ asmsub chrout(ubyte character @ A) {
|
|||
pha
|
||||
_tmp_outchar
|
||||
lda #0
|
||||
jsr outchar
|
||||
jsr atari.outchar
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
|
@ -148,15 +151,15 @@ _tmp_outchar
|
|||
}
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; ---- print zero terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
- lda (P8ZP_SCRATCH_W2),y
|
||||
beq +
|
||||
jsr chrout
|
||||
iny
|
||||
|
@ -168,13 +171,13 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
|||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
jsr conv.internal_ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr chrout
|
||||
pla
|
||||
jsr chrout
|
||||
txa
|
||||
jsr chrout
|
||||
pla
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
@ -182,21 +185,21 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
|||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
jsr conv.internal_ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr chrout
|
||||
pla
|
||||
txa
|
||||
jsr chrout
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
+ cpx #'0'
|
||||
beq _ones
|
||||
txa
|
||||
jsr chrout
|
||||
_ones txa
|
||||
_ones pla
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
@ -210,7 +213,7 @@ asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
|||
lda #'-'
|
||||
jsr chrout
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jsr conv.internal_byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
@ -223,7 +226,7 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
|
|||
lda #'$'
|
||||
jsr chrout
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
+ jsr conv.internal_ubyte2hex
|
||||
jsr chrout
|
||||
tya
|
||||
jmp chrout
|
||||
|
@ -277,9 +280,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
|||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
jsr conv.internal_uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
- lda conv.internal_uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr chrout
|
||||
iny
|
||||
|
@ -291,9 +294,9 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
|||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
jsr conv.internal_uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
- lda conv.internal_uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
|
@ -303,7 +306,7 @@ asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
|||
_gotdigit
|
||||
jsr chrout
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
lda conv.internal_uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
|
@ -385,13 +388,19 @@ sub setcc (ubyte col, ubyte row, ubyte char, ubyte charcolor) {
|
|||
}
|
||||
|
||||
sub plot(ubyte col, ubyte rownum) {
|
||||
column(col)
|
||||
row(rownum)
|
||||
column(col)
|
||||
row(rownum)
|
||||
}
|
||||
|
||||
sub get_cursor(uword colptr, uword rowptr) {
|
||||
@(colptr) = get_column()
|
||||
@(rowptr) = get_row()
|
||||
@(colptr) = get_column()
|
||||
@(rowptr) = get_row()
|
||||
}
|
||||
|
||||
asmsub waitkey() -> ubyte @A {
|
||||
%asm {{
|
||||
jmp atari.waitkey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user