mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
566 Commits
v10.4.1
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
837e88d61d | |||
399cf5118d | |||
a87f2640d3 | |||
a90ef274d7 | |||
341778ba67 | |||
ec50b5a007 | |||
31d84c8921 | |||
34bedbeef1 | |||
3b1b0985c1 | |||
368387e1a7 | |||
9da430ffeb | |||
cc063124cf | |||
3b37b89951 | |||
844b537d1e | |||
caf1d4a22a | |||
d8e244df99 | |||
548e421e27 | |||
322fa7ea69 | |||
cf7bea0985 | |||
25d7f8808f | |||
acc630972a | |||
6a33be3fd8 | |||
f5fc4e345c | |||
67231af623 | |||
e31ef6f06f | |||
09d188106a | |||
d8e2116481 | |||
435dfbb932 | |||
ba93966474 | |||
ea8d17cdb2 | |||
082265fb25 | |||
9e557ce8ac | |||
e5d9af75de | |||
31c1bf8bc5 | |||
37d4055036 | |||
78b1076110 | |||
0a3c748e41 | |||
ebf79ef9e2 | |||
99b9370178 | |||
d634061cd9 | |||
d59d8ff1fe | |||
53e442d509 | |||
f7cbfdff06 | |||
b28ee0819f | |||
522958e0e9 | |||
ccc6b56e35 | |||
7eb079050c | |||
2fdd5543b2 | |||
d04164c0a6 | |||
b047731f82 | |||
4d91f92a2e | |||
98505d27b1 | |||
cd63a58ad9 | |||
170f8dd092 | |||
619dcb6a84 | |||
99ae8ea52e | |||
dc031c30eb | |||
1e702439b7 | |||
8debc42381 | |||
532d719089 | |||
b40860aca4 | |||
2cbe6b5f7f | |||
d2cc7ccdfa | |||
2cb183c6d8 | |||
84026b105f | |||
a4d0589f10 | |||
e375f6afce | |||
5a7bc04816 | |||
bd1894580e | |||
9e694c0337 | |||
c82586db28 | |||
dd2d466350 | |||
830da8de0a | |||
4e5ee333c8 | |||
9df899eb63 | |||
ca7491a702 | |||
1a07129865 | |||
4fbd67ff99 | |||
5bc6c50f42 | |||
063de3801d | |||
ae65266a4a | |||
8ed2401e0b | |||
d2e8ee8269 | |||
1f996e3b8b | |||
7108b74105 | |||
801fe1b604 | |||
fb44c87597 | |||
6b9cdbd482 | |||
0ab98033b5 | |||
14a2b96609 | |||
f829b689db | |||
dfda8b7ed5 | |||
4388466451 | |||
5c2f509a52 | |||
59582f5210 | |||
e2a8bdbdfb | |||
0916b943da | |||
9c7ebc883c | |||
0ee42b9aa0 | |||
37b3868ca3 | |||
a6835ce3f0 | |||
69c96ad99b | |||
b72877d59d | |||
05eb15d4f7 | |||
f1fec37c79 | |||
73f6880ff8 | |||
8a53742f31 | |||
9be40e85ff | |||
61079c1eb7 | |||
1075ee8fc3 | |||
a28b265197 | |||
20e534c468 | |||
da7aa5dc49 | |||
8f2a43ca0a | |||
d0909d7810 | |||
1641999d20 | |||
e16452037c | |||
344d79684a | |||
573a1d9b7b | |||
25ab57580c | |||
a332e0e3d1 | |||
376f1cb139 | |||
90f80558d7 | |||
e281994898 | |||
29fac122e1 | |||
1dc412eb90 | |||
3770a4fe0c | |||
79cda544c8 | |||
f04b97d890 | |||
3e9b4ccc45 | |||
2c3d838dd8 | |||
7668a3c660 | |||
5dd45b714a | |||
8b08895d0f | |||
8f8d99e3ed | |||
23474360ec | |||
81c255c450 | |||
ef23d52ed7 | |||
220ab773aa | |||
e3e5bff7bb | |||
7b9a841b2a | |||
40423911ef | |||
582a70b046 | |||
5b63590ebf | |||
2b6510dc19 | |||
9d49589d73 | |||
125b66c929 | |||
5255f1c052 | |||
a6ba05d60c | |||
41e963b04b | |||
6ff75bef29 | |||
72c16d0d32 | |||
94653e5c8c | |||
3e2b2a698d | |||
ae04f5aee8 | |||
5c56267662 | |||
e55ce5504e | |||
fb1e89d9ef | |||
bc550a4549 | |||
ebdea9cf76 | |||
09ec508f82 | |||
d06e9ea7f6 | |||
a36bdc54fd | |||
0814ea9711 | |||
daefe839d8 | |||
e6088dd315 | |||
fc03d6f332 | |||
2aeb7a838e | |||
99ff5dd078 | |||
49982b49b6 | |||
fd39c22616 | |||
9e79722a7f | |||
17334a1c58 | |||
c7f0ff11ac | |||
cd2cc89e6a | |||
069143092d | |||
efd41260f2 | |||
8d2410622c | |||
60554389b3 | |||
a940dc7d43 | |||
06ca68a625 | |||
5b58e5b158 | |||
74dd8fe80b | |||
75ddcda5f3 | |||
216825b98a | |||
a96defab86 | |||
0864b0a1b7 | |||
8b158d9240 | |||
f335251c2b | |||
67bc0b6931 | |||
e646dd1ed1 | |||
2b7947f9b0 | |||
ec0cfb4b3f | |||
9cdf53019c | |||
1a04a3eb3a | |||
105d3995e0 | |||
8ce3204f93 | |||
d0f15f1285 | |||
66d6f67120 | |||
a106c88054 | |||
ee784e1ccc | |||
bb75be0b44 | |||
2478aea316 | |||
1e17df5296 | |||
8583a96519 | |||
d0c184c7de | |||
0191acb2b3 | |||
277a1a32b2 | |||
7a13f57ab0 | |||
0c882836d9 | |||
228be5cd04 | |||
08cd2fd6e8 | |||
bc7b086f0f | |||
e8f3af6981 | |||
f9c7c7dab7 | |||
09a17743ad | |||
4f096a7511 | |||
2ab2130000 | |||
66558f7638 | |||
a6f9ed07e7 | |||
7268a8736f | |||
8f6b5676d7 | |||
ca9422bbe9 | |||
35d9412559 | |||
f071c07dd9 | |||
e5ff3c1ff3 | |||
f0e8ff0326 | |||
3b5cda85ff | |||
420793f9e2 | |||
cf1dbaf0d8 | |||
d187cef6b7 | |||
3b3616afda | |||
0ffebc25d0 | |||
478e2b4ebd | |||
a56ae7539a | |||
407773bda2 | |||
823eaa8918 | |||
a2be42c5ca | |||
a76b8d66ff | |||
b7f47d354f | |||
5d33c93af9 | |||
4db6859f3f | |||
45fe1bb16e | |||
b014facbd3 | |||
3b4b37f16b | |||
68d5983a14 | |||
f2cfcfdf31 | |||
10b9162dc5 | |||
c84cc8f8c9 | |||
78c71bbf0e | |||
37c2c1bf0b | |||
c8996418da | |||
76b29aa629 | |||
ee521793f8 | |||
f42e12bc13 | |||
427451a23f | |||
af7930d494 | |||
e2882d37bf | |||
942d3ee640 | |||
7b4a82b91a | |||
056c0a24d9 | |||
827df04b32 | |||
e174b31344 | |||
49959af752 | |||
c86c0912f8 | |||
268b0c9365 | |||
099fe280ba | |||
f786f60e9c | |||
f40e1eb1f2 | |||
8b9da65357 | |||
2cbbe0d48a | |||
b6e1fb3ba8 | |||
bdccffbb8e | |||
5a85474712 | |||
f50899c6fa | |||
4daa909f32 | |||
4555edf369 | |||
529ea5bf58 | |||
fe011de934 | |||
0653d430a7 | |||
a587f6e9a0 | |||
3850e1dbb5 | |||
91cde072e0 | |||
2ca4aed566 | |||
5071da6784 | |||
4c1e2f3110 | |||
2727a4dcb3 | |||
126d4c69e6 | |||
7657edcb7d | |||
580e786952 | |||
c0ae35b3a3 | |||
c3dc74788a | |||
379d241a0d | |||
1f49e8fe75 | |||
d70cfbb661 | |||
5482ac0302 | |||
131d5ceb4f | |||
512ddd1694 | |||
14a213bff9 | |||
d586846bc5 | |||
ef4efcb112 | |||
b01555d75e | |||
3804fba0f1 | |||
f93b7e3303 | |||
73baaeff1f | |||
7c79cdbd2f | |||
8ea032ed66 | |||
e7a0cb636c | |||
02f3f5d0f5 | |||
1e9bbd662b | |||
8644a4ae91 | |||
1e85f7812f | |||
80d88b3c61 | |||
d2827a7431 | |||
28c721fa7d | |||
8f799567cf | |||
9e8cc8b54d | |||
cc59069876 | |||
697d54e10a | |||
1679ca79b4 | |||
124ec77b58 | |||
3675d7961b | |||
f8aaa2d13c | |||
b7afda781a | |||
535ec13072 | |||
26d0a174db | |||
b2e821755c | |||
2e303041c1 | |||
96bed8f57f | |||
86d4a4309f | |||
1a1ab0dac6 | |||
ba8c3d14f7 | |||
617ea15c3a | |||
ef192a5778 | |||
565973c520 | |||
25b1043572 | |||
1ebfff7c7b | |||
8341f9c066 | |||
28cac291de | |||
8fa14a10e2 | |||
55dbd095ed | |||
31ad8bdd8d | |||
181f3e9eb1 | |||
50c3d809dc | |||
58f696d00a | |||
f603c543d3 | |||
6aaa0f928e | |||
feb8aa435e | |||
310e8f15cd | |||
da03941582 | |||
dcbb36a3bd | |||
53558f5c1d | |||
189399d5f8 | |||
5406a992f5 | |||
bc9683cc54 | |||
2eed75f602 | |||
d58f9f56c4 | |||
2e35f3c3a3 | |||
5c6bd9c091 | |||
857d2eefca | |||
90f1e7fd6a | |||
18e37accf9 | |||
cc53d698bf | |||
cb86206698 | |||
d77b1944fb | |||
a58cb43c4a | |||
88574c87c4 | |||
3a7a7091c0 | |||
906b137a7c | |||
42e2c5f605 | |||
cc13a51493 | |||
f569ce6141 | |||
4958463e75 | |||
2360625927 | |||
8badc40883 | |||
844c97930f | |||
5c09dc10ae | |||
9fd9e9ab5f | |||
35c477b5a6 | |||
ae0cadb383 | |||
984230e8fa | |||
a874aec6a1 | |||
ea1daa97d3 | |||
fb0d9b46b0 | |||
9da70bdf05 | |||
d640cfbe13 | |||
51a05ec4b7 | |||
1f5706bbeb | |||
25c9b2fea4 | |||
154f9b300f | |||
d78ce77536 | |||
b4fb43bc80 | |||
e0e01f794e | |||
08865dbb4e | |||
b9ad7e0e55 | |||
07158a6f1a | |||
957c42bc1d | |||
f784da2da6 | |||
c080fbe59a | |||
d70b8303b1 | |||
1d38c3582a | |||
9438e996d7 | |||
3b4a5e27f7 | |||
648d9fc269 | |||
54fccec7d7 | |||
4f9693055e | |||
555c50ee10 | |||
bf98ceca2c | |||
573cecb087 | |||
1b528491c2 | |||
4bdabe1961 | |||
a3fa527378 | |||
84f5ffa426 | |||
25d2b42283 | |||
300d1a871c | |||
2fcb83a39f | |||
3ba1d00a7c | |||
64164c1c72 | |||
3ee6058524 | |||
93a0a41e73 | |||
e7ab7b6d7a | |||
7d4dc3c063 | |||
a50400b7d1 | |||
f89f1a84d0 | |||
688dce6145 | |||
b88f550c5b | |||
9864abd393 | |||
c702c4a6df | |||
77e376f6bf | |||
491e5dbcfb | |||
a5c7393561 | |||
7fd3e9bb7d | |||
459e9f8f3b | |||
5b1143bcb3 | |||
fddd390d31 | |||
e514eeba17 | |||
c11a52b278 | |||
85e87dfe2e | |||
cb47e2c149 | |||
0fc9aa6b2d | |||
155896c4c7 | |||
178e60bba0 | |||
9f84aa5fb2 | |||
66fc109ce5 | |||
a231872821 | |||
7cfb33a448 | |||
3b798097b9 | |||
6fb05bdefc | |||
64ea72ed4d | |||
89425088ce | |||
925b9d845d | |||
ad074076c2 | |||
a2194c43a6 | |||
4b23b1dc86 | |||
9005c7994a | |||
4a47e15b1c | |||
09cbdf410a | |||
df6a43c7f0 | |||
4ce130dc8b | |||
94d76aa82c | |||
73609636c5 | |||
66b06d6c40 | |||
eeeb8d81f4 | |||
6f727aff88 | |||
518e5a30c2 | |||
bbba4b3d60 | |||
967adb9a87 | |||
040a6c62de | |||
483d193ced | |||
62458216c9 | |||
76b05cb5fd | |||
570b574b93 | |||
a82f211f9a | |||
504c80cddf | |||
4b4af9b527 | |||
28b383f888 | |||
40ce7725a1 | |||
1f2d46628e | |||
c9535049c8 | |||
9317cf8a35 | |||
1cd754f05d | |||
97b8cb748d | |||
84d9040b57 | |||
fdd18c615c | |||
c14f6cfc2b | |||
326eab3dd1 | |||
6da1f7eb4c | |||
1e82483152 | |||
6e2fd41a8b | |||
9927af1095 | |||
7585b6ef6f | |||
a6159702da | |||
0247fb0d84 | |||
6de760885f | |||
9851d14fb9 | |||
d5fc69d3e4 | |||
a40d120f2a | |||
fcdd9414d9 | |||
272a1001a8 | |||
2a52241f1c | |||
d8f1822c12 | |||
ce7d094adb | |||
a0cf1889a3 | |||
38ef394e15 | |||
abbf7c7cb0 | |||
ca5f7ae32f | |||
cbc4b75e50 | |||
65ddcf91d0 | |||
5280e1b449 | |||
b6ffb81909 | |||
e9edffa9f0 | |||
0dd1c17ff4 | |||
aef211e5f3 | |||
66829203d8 | |||
7a0eaf3148 | |||
fa5479ee5f | |||
03412cacba | |||
01a38a0b11 | |||
f43c14bd78 | |||
fb23452383 | |||
ab7dde1450 | |||
8d9bc2f5ff | |||
7651ccc84e | |||
1a6b95b388 | |||
78ec1e7512 | |||
7e38d26c33 | |||
ed09dd4e9e | |||
5731b79554 | |||
eaa22a9d13 | |||
b2bdfe8482 | |||
fea531be9a | |||
7c69d38588 | |||
a088ee56b0 | |||
ae669af904 | |||
d1ddf05e38 | |||
51279a98b3 | |||
bf33a4f82d | |||
fff0d741c3 | |||
e83d0ee820 | |||
09f3eecf56 | |||
2bd4326ff6 | |||
c13168b60c | |||
ea3871d0c4 | |||
70a2b11271 | |||
3cf39e072e | |||
413b86cc4a | |||
a6107fcfdf | |||
a064ade1e0 | |||
df35aa7942 | |||
cd49c5f88d | |||
1541ad2160 | |||
c78b7b1a24 | |||
9c7a645e18 | |||
4acf38031a | |||
4cd7271e30 | |||
3f630ab1b0 | |||
04cb684fd4 | |||
4c843571ea | |||
1326498802 | |||
b7ebd8c4a6 | |||
24e0a69480 | |||
4bcb2bdede | |||
d27f3eb8a4 | |||
d3e4481112 | |||
1d1d6b3d98 | |||
90b8a22a71 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
#patreon: # Replace with a single Patreon username
|
#patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
#open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: irmen
|
ko_fi: irmen
|
||||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
@ -11,3 +11,4 @@ ko_fi: irmen
|
|||||||
#otechie: # Replace with a single Otechie username
|
#otechie: # Replace with a single Otechie username
|
||||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
custom: ['https://paypal.me/irmendejong']
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,8 +1,12 @@
|
|||||||
.idea/workspace.xml
|
.idea/workspace.xml
|
||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
|
.idea/developer-tools.xml
|
||||||
|
.idea/usage.statistics.xml
|
||||||
|
.idea/shelf/
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
output/
|
output/
|
||||||
|
out/
|
||||||
.*cache/
|
.*cache/
|
||||||
*.directory
|
*.directory
|
||||||
*.prg
|
*.prg
|
||||||
@ -11,7 +15,6 @@ output/
|
|||||||
*.vm.txt
|
*.vm.txt
|
||||||
*.vice-mon-list
|
*.vice-mon-list
|
||||||
docs/build
|
docs/build
|
||||||
out/
|
|
||||||
parser/**/*.interp
|
parser/**/*.interp
|
||||||
parser/**/*.tokens
|
parser/**/*.tokens
|
||||||
parser/**/*.java
|
parser/**/*.java
|
||||||
@ -22,6 +25,7 @@ compiler/src/prog8/buildversion/*
|
|||||||
.eggs/
|
.eggs/
|
||||||
/MANIFEST
|
/MANIFEST
|
||||||
.tox/
|
.tox/
|
||||||
|
.kotlin/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
parser.out
|
parser.out
|
||||||
parsetab.py
|
parsetab.py
|
||||||
@ -30,7 +34,6 @@ parsetab.py
|
|||||||
compiler/lib/
|
compiler/lib/
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
/prog8compiler.jar
|
|
||||||
sd*.img
|
sd*.img
|
||||||
*.d64
|
*.d64
|
||||||
|
|
||||||
|
1458
.idea/inspectionProfiles/Project_Default.xml
generated
1458
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
12
.idea/kotlinc.xml
generated
12
.idea/kotlinc.xml
generated
@ -1,9 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="Kotlin2JsCompilerArguments">
|
||||||
|
<option name="moduleKind" value="plain" />
|
||||||
|
</component>
|
||||||
<component name="Kotlin2JvmCompilerArguments">
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
<option name="jvmTarget" value="11" />
|
<option name="jvmTarget" value="11" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="KotlinCommonCompilerArguments">
|
||||||
|
<option name="apiVersion" value="2.1" />
|
||||||
|
<option name="languageVersion" value="2.1" />
|
||||||
|
</component>
|
||||||
|
<component name="KotlinCompilerSettings">
|
||||||
|
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||||
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.9.24" />
|
<option name="version" value="2.1.10" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
20
.idea/libraries/KotlinJavaRuntime.xml
generated
20
.idea/libraries/KotlinJavaRuntime.xml
generated
@ -1,23 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="KotlinJavaRuntime" type="repository">
|
<library name="KotlinJavaRuntime" type="repository">
|
||||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.20" />
|
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
13
.idea/libraries/eclipse_lsp4j.xml
generated
Normal file
13
.idea/libraries/eclipse_lsp4j.xml
generated
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="eclipse.lsp4j" type="repository">
|
||||||
|
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
16
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
16
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
@ -1,12 +1,20 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC>
|
||||||
<SOURCES />
|
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@ -4,13 +4,13 @@
|
|||||||
<option name="perGrammarGenerationSettings">
|
<option name="perGrammarGenerationSettings">
|
||||||
<list>
|
<list>
|
||||||
<PerGrammarGenerationSettings>
|
<PerGrammarGenerationSettings>
|
||||||
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
|
<option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" />
|
||||||
<option name="autoGen" value="true" />
|
<option name="autoGen" value="true" />
|
||||||
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
||||||
<option name="libDir" value="" />
|
<option name="libDir" value="" />
|
||||||
<option name="encoding" value="" />
|
<option name="encoding" value="" />
|
||||||
<option name="pkg" value="" />
|
<option name="pkg" value="" />
|
||||||
<option name="language" value="" />
|
<option name="language" value="Java" />
|
||||||
<option name="generateListener" value="false" />
|
<option name="generateListener" value="false" />
|
||||||
</PerGrammarGenerationSettings>
|
</PerGrammarGenerationSettings>
|
||||||
</list>
|
</list>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<type id="Python" />
|
<type id="Python" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
5
.idea/modules.xml
generated
5
.idea/modules.xml
generated
@ -2,6 +2,8 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||||
@ -13,7 +15,10 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
|
4
Makefile
4
Makefile
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
all:
|
all:
|
||||||
gradle installdist installshadowdist
|
gradle installdist installshadowdist
|
||||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
|
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
gradle build
|
gradle build
|
||||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
|
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||||
|
|
||||||
|
37
README.md
37
README.md
@ -1,4 +1,7 @@
|
|||||||
[](https://ko-fi.com/H2H6S0FFF)
|
[](https://ko-fi.com/H2H6S0FFF)
|
||||||
|
|
||||||
|
PayPal: [](https://paypal.me/irmendejong)
|
||||||
|
|
||||||
[](https://prog8.readthedocs.io/)
|
[](https://prog8.readthedocs.io/)
|
||||||
|
|
||||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||||
@ -14,7 +17,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
|||||||
|
|
||||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
@ -54,27 +57,31 @@ What does Prog8 provide?
|
|||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- all advantages of a higher level language over having to write assembly code manually
|
- all advantages of a higher level language over having to write assembly code manually
|
||||||
- programs run very fast because compilation to native machine code
|
- programs run very fast because it's compiled to native machine code
|
||||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||||
- modularity, symbol scoping, subroutines
|
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
- floating point math is supported on certain targets
|
||||||
|
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||||
|
- tight control over Zeropage usage
|
||||||
|
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
|
- programs can be configured to execute in ROM
|
||||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||||
- subroutines with input parameters and result values
|
|
||||||
- high-level program optimizations
|
- high-level program optimizations
|
||||||
- no need for forward declarations
|
- conditional branches that map 1:1 to cpu status flags
|
||||||
- small program boilerplate/compilersupport overhead
|
|
||||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
|
||||||
- conditional branches
|
|
||||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||||
|
- ``on .. goto`` statement for fast jump tables
|
||||||
- ``in`` expression for concise and efficient multi-value/containment check
|
- ``in`` expression for concise and efficient multi-value/containment check
|
||||||
|
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
- subroutines can return more than one result value
|
||||||
- inline assembly allows you to have full control when every cycle or byte matters
|
- inline assembly allows you to have full control when every cycle or byte matters
|
||||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
|
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||||
- encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
|
- encode strings and characters into petscii or screencodes or even other encodings
|
||||||
|
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
|
||||||
|
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
|
||||||
|
|
||||||
*Rapid edit-compile-run-debug cycle:*
|
*Rapid edit-compile-run-debug cycle:*
|
||||||
|
|
||||||
@ -85,11 +92,11 @@ What does Prog8 provide?
|
|||||||
|
|
||||||
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||||
|
|
||||||
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||||
- "c64": Commodore-64 (6502 like CPU)
|
- "c64": Commodore-64 (6502 like CPU)
|
||||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
- "pet32": Commodore PET (limited support)
|
||||||
- "pet32": Commodore PET (experimental)
|
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
|
||||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||||
|
|
||||||
|
|
||||||
|
24
beanshell/beanshell.iml
Normal file
24
beanshell/beanshell.iml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
<orderEntry type="module-library">
|
||||||
|
<library>
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MODULE_DIR$/lib/bsh-3.0.0-SNAPSHOT.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</orderEntry>
|
||||||
|
</component>
|
||||||
|
</module>
|
65
beanshell/build.gradle.kts
Normal file
65
beanshell/build.gradle.kts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("application")
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
val serverMainClassName = "prog8lsp.MainKt"
|
||||||
|
val applicationName = "prog8-beanshell"
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set(serverMainClassName)
|
||||||
|
description = "Code completions, diagnostics and more for Prog8"
|
||||||
|
// applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version")
|
||||||
|
applicationDistribution.into("bin") {
|
||||||
|
filePermissions {
|
||||||
|
user {
|
||||||
|
read=true
|
||||||
|
execute=true
|
||||||
|
write=true
|
||||||
|
}
|
||||||
|
other.execute = true
|
||||||
|
group.execute = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(files("lib/bsh-3.0.0-SNAPSHOT.jar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.forEach { config ->
|
||||||
|
config.resolutionStrategy {
|
||||||
|
preferProjectModules()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.main {
|
||||||
|
java.srcDir("src")
|
||||||
|
resources.srcDir("resources")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.startScripts {
|
||||||
|
applicationName = "prog8-beanshell"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Exec>("fixFilePermissions") {
|
||||||
|
// When running on macOS or Linux the start script
|
||||||
|
// needs executable permissions to run.
|
||||||
|
|
||||||
|
onlyIf { !System.getProperty("os.name").lowercase().contains("windows") }
|
||||||
|
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.installDist {
|
||||||
|
finalizedBy("fixFilePermissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.build {
|
||||||
|
finalizedBy("installDist")
|
||||||
|
}
|
BIN
beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
Normal file
BIN
beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
Normal file
Binary file not shown.
48
beanshell/src/prog8beanshell/CommandLineReader.kt
Normal file
48
beanshell/src/prog8beanshell/CommandLineReader.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package prog8beanshell
|
||||||
|
|
||||||
|
import java.io.FilterReader
|
||||||
|
import java.io.Reader
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLineReader(val input: Reader): FilterReader(input) {
|
||||||
|
private val normal = 0
|
||||||
|
private val lastCharNL = 1
|
||||||
|
private val sentSemi = 2
|
||||||
|
private var state = lastCharNL
|
||||||
|
|
||||||
|
override fun read(): Int {
|
||||||
|
if (state == sentSemi) {
|
||||||
|
this.state = lastCharNL
|
||||||
|
return 10
|
||||||
|
} else {
|
||||||
|
var b = input.read()
|
||||||
|
while(b==13) b = input.read()
|
||||||
|
|
||||||
|
if (b == 10) {
|
||||||
|
if (this.state == lastCharNL) {
|
||||||
|
b = 59
|
||||||
|
this.state = sentSemi
|
||||||
|
} else {
|
||||||
|
this.state = lastCharNL
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.state = normal
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(buff: CharArray, off: Int, len: Int): Int {
|
||||||
|
val b = read()
|
||||||
|
if (b == -1) {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
buff[off] = b.toChar()
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
23
beanshell/src/prog8beanshell/Main.kt
Normal file
23
beanshell/src/prog8beanshell/Main.kt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package prog8beanshell
|
||||||
|
|
||||||
|
import bsh.FileReader
|
||||||
|
import bsh.Interpreter
|
||||||
|
|
||||||
|
|
||||||
|
class BeanshellInterpreter {
|
||||||
|
|
||||||
|
fun run(symbols: Map<String, Any>) {
|
||||||
|
val interpreter = Interpreter(CommandLineReader(FileReader(System.`in`)), System.out, System.err, true)
|
||||||
|
interpreter.setExitOnEOF(false)
|
||||||
|
symbols.forEach { (name, value) -> interpreter.set(name, value) }
|
||||||
|
interpreter.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val i = BeanshellInterpreter()
|
||||||
|
i.run(mapOf(
|
||||||
|
"env" to System.getenv(),
|
||||||
|
"args" to args
|
||||||
|
))
|
||||||
|
}
|
10
benchmark-program/Makefile
Normal file
10
benchmark-program/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.PHONY: clean run
|
||||||
|
|
||||||
|
run:
|
||||||
|
prog8c -target cx16 benchmark.p8
|
||||||
|
x16emu -run -prg benchmark.prg -warp
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z
|
||||||
|
|
||||||
|
|
109
benchmark-program/b_3d.p8
Normal file
109
benchmark-program/b_3d.p8
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
%import textio
|
||||||
|
%import math
|
||||||
|
|
||||||
|
rotate3d {
|
||||||
|
const ubyte WIDTH = 40
|
||||||
|
const ubyte HEIGHT = 30
|
||||||
|
|
||||||
|
sub benchmark(uword max_time) -> uword {
|
||||||
|
|
||||||
|
uword anglex
|
||||||
|
uword angley
|
||||||
|
uword anglez
|
||||||
|
uword frames
|
||||||
|
|
||||||
|
txt.nl()
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
matrix_math.rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
||||||
|
draw_edges() ; doesn't really draw anything in the benchmark, but does do the screen calculations
|
||||||
|
anglex+=500
|
||||||
|
angley+=215
|
||||||
|
anglez+=453
|
||||||
|
frames++
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames
|
||||||
|
}
|
||||||
|
|
||||||
|
sub draw_edges() {
|
||||||
|
|
||||||
|
; plot the points of the 3d cube
|
||||||
|
; first the points on the back, then the points on the front (painter algorithm)
|
||||||
|
|
||||||
|
ubyte @zp i
|
||||||
|
word @zp rz
|
||||||
|
word @zp persp
|
||||||
|
byte @shared sx
|
||||||
|
byte @shared sy
|
||||||
|
|
||||||
|
for i in 0 to len(matrix_math.xcoor)-1 {
|
||||||
|
rz = matrix_math.rotatedz[i]
|
||||||
|
if rz >= 10 {
|
||||||
|
persp = 600 + rz/64
|
||||||
|
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
|
||||||
|
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
|
||||||
|
;; txt.setcc(sx as ubyte, sy as ubyte, 46, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 to len(matrix_math.xcoor)-1 {
|
||||||
|
rz = matrix_math.rotatedz[i]
|
||||||
|
if rz < 10 {
|
||||||
|
persp = 600 + rz/64
|
||||||
|
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
|
||||||
|
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
|
||||||
|
;; txt.setcc(sx as ubyte, sy as ubyte, 81, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txt.chrout('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix_math {
|
||||||
|
; vertices
|
||||||
|
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
|
||||||
|
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
|
||||||
|
word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
|
||||||
|
|
||||||
|
; storage for rotated coordinates
|
||||||
|
word[len(xcoor)] rotatedx
|
||||||
|
word[len(ycoor)] rotatedy
|
||||||
|
word[len(zcoor)] rotatedz
|
||||||
|
|
||||||
|
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
|
||||||
|
; rotate around origin (0,0,0)
|
||||||
|
|
||||||
|
; set up the 3d rotation matrix values
|
||||||
|
word wcosa = math.cos8(ax)
|
||||||
|
word wsina = math.sin8(ax)
|
||||||
|
word wcosb = math.cos8(ay)
|
||||||
|
word wsinb = math.sin8(ay)
|
||||||
|
word wcosc = math.cos8(az)
|
||||||
|
word wsinc = math.sin8(az)
|
||||||
|
|
||||||
|
word wcosa_sinb = wcosa*wsinb / 128
|
||||||
|
word wsina_sinb = wsina*wsinb / 128
|
||||||
|
|
||||||
|
word Axx = wcosa*wcosb / 128
|
||||||
|
word Axy = (wcosa_sinb*wsinc - wsina*wcosc) / 128
|
||||||
|
word Axz = (wcosa_sinb*wcosc + wsina*wsinc) / 128
|
||||||
|
word Ayx = wsina*wcosb / 128
|
||||||
|
word Ayy = (wsina_sinb*wsinc + wcosa*wcosc) / 128
|
||||||
|
word Ayz = (wsina_sinb*wcosc - wcosa*wsinc) / 128
|
||||||
|
word Azx = -wsinb
|
||||||
|
word Azy = wcosb*wsinc / 128
|
||||||
|
word Azz = wcosb*wcosc / 128
|
||||||
|
|
||||||
|
ubyte @zp i
|
||||||
|
for i in 0 to len(xcoor)-1 {
|
||||||
|
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||||
|
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||||
|
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
|
||||||
|
rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
89
benchmark-program/b_adpcm.p8
Normal file
89
benchmark-program/b_adpcm.p8
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
adpcm {
|
||||||
|
|
||||||
|
sub decode_benchmark(uword max_time) -> uword {
|
||||||
|
uword num_blocks
|
||||||
|
txt.nl()
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
adpcm.init(0,0)
|
||||||
|
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
|
||||||
|
repeat 252/2 {
|
||||||
|
unroll 2 {
|
||||||
|
ubyte @zp nibble = @(nibbles_ptr)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word (note: upper nibble needs to be zero!)
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word (note: upper nibble is zero, after the shifts.)
|
||||||
|
nibbles_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num_blocks++
|
||||||
|
txt.chrout('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
; IMA ADPCM decoder. Supports mono and stereo streams.
|
||||||
|
|
||||||
|
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
|
||||||
|
uword[] t_step = [
|
||||||
|
7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
|
16, 17, 19, 21, 23, 25, 28, 31,
|
||||||
|
34, 37, 41, 45, 50, 55, 60, 66,
|
||||||
|
73, 80, 88, 97, 107, 118, 130, 143,
|
||||||
|
157, 173, 190, 209, 230, 253, 279, 307,
|
||||||
|
337, 371, 408, 449, 494, 544, 598, 658,
|
||||||
|
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||||
|
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||||
|
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||||
|
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||||
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||||
|
32767]
|
||||||
|
|
||||||
|
uword @requirezp predict ; decoded 16 bit pcm sample for first channel.
|
||||||
|
ubyte @requirezp index
|
||||||
|
uword @requirezp pstep
|
||||||
|
|
||||||
|
sub init(uword startPredict, ubyte startIndex) {
|
||||||
|
; initialize first decoding channel.
|
||||||
|
predict = startPredict
|
||||||
|
index = startIndex
|
||||||
|
pstep = t_step[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
sub decode_nibble(ubyte @zp nibble) {
|
||||||
|
; Decoder for a single nibble for the first channel. (value of 'nibble' needs to be strictly 0-15 !)
|
||||||
|
; This is the hotspot of the decoder algorithm!
|
||||||
|
; Note that the generated assembly from this is pretty efficient,
|
||||||
|
; rewriting it by hand in asm seems to improve it only ~10%.
|
||||||
|
cx16.r0s = 0 ; difference
|
||||||
|
if nibble & %0100 !=0
|
||||||
|
cx16.r0s += pstep
|
||||||
|
pstep >>= 1
|
||||||
|
if nibble & %0010 !=0
|
||||||
|
cx16.r0s += pstep
|
||||||
|
pstep >>= 1
|
||||||
|
if nibble & %0001 !=0
|
||||||
|
cx16.r0s += pstep
|
||||||
|
pstep >>= 1
|
||||||
|
cx16.r0s += pstep
|
||||||
|
if nibble & %1000 !=0
|
||||||
|
predict -= cx16.r0
|
||||||
|
else
|
||||||
|
predict += cx16.r0
|
||||||
|
|
||||||
|
; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word
|
||||||
|
; but for speed reasons we only work with 16 bit words here all the time (with possible clipping error)
|
||||||
|
; if predicted > 32767:
|
||||||
|
; predicted = 32767
|
||||||
|
; elif predicted < -32767:
|
||||||
|
; predicted = - 32767
|
||||||
|
|
||||||
|
index += t_index[nibble] as ubyte
|
||||||
|
if_neg
|
||||||
|
index = 0
|
||||||
|
else if index >= len(t_step)-1
|
||||||
|
index = len(t_step)-1
|
||||||
|
pstep = t_step[index]
|
||||||
|
}
|
||||||
|
}
|
111
benchmark-program/b_circles.p8
Normal file
111
benchmark-program/b_circles.p8
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
%import gfx_lores
|
||||||
|
%import math
|
||||||
|
|
||||||
|
circles {
|
||||||
|
const ubyte MAX_NUM_CIRCLES = 80
|
||||||
|
const ubyte GROWTH_RATE = 4
|
||||||
|
uword[MAX_NUM_CIRCLES] circle_x
|
||||||
|
uword[MAX_NUM_CIRCLES] circle_y
|
||||||
|
ubyte[MAX_NUM_CIRCLES] circle_radius
|
||||||
|
ubyte color
|
||||||
|
uword total_num_circles
|
||||||
|
|
||||||
|
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||||
|
if use_kernal
|
||||||
|
cx16.set_screen_mode(128)
|
||||||
|
else
|
||||||
|
gfx_lores.graphics_mode()
|
||||||
|
|
||||||
|
math.rndseed(12345,6789)
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
total_num_circles = 0
|
||||||
|
color = 16
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
if use_kernal {
|
||||||
|
cx16.GRAPH_set_colors(0,0,0)
|
||||||
|
cx16.GRAPH_clear()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gfx_lores.clear_screen(0)
|
||||||
|
total_num_circles += draw_circles(use_kernal, max_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
if use_kernal
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
else {
|
||||||
|
gfx_lores.text_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_num_circles
|
||||||
|
}
|
||||||
|
|
||||||
|
sub draw_circles(bool use_kernal, uword max_time) -> uword {
|
||||||
|
uword @zp x
|
||||||
|
uword @zp y
|
||||||
|
ubyte @zp radius
|
||||||
|
|
||||||
|
ubyte num_circles
|
||||||
|
|
||||||
|
while num_circles<MAX_NUM_CIRCLES and cbm.RDTIM16()<max_time {
|
||||||
|
x = math.rndw() % 320
|
||||||
|
y = math.rndw() % 240
|
||||||
|
radius = GROWTH_RATE
|
||||||
|
if not_colliding() {
|
||||||
|
while not_edge() and not_colliding() {
|
||||||
|
radius += GROWTH_RATE
|
||||||
|
}
|
||||||
|
radius -= GROWTH_RATE
|
||||||
|
if radius>0 {
|
||||||
|
color++
|
||||||
|
if color==0
|
||||||
|
color=16
|
||||||
|
if use_kernal {
|
||||||
|
cx16.GRAPH_set_colors(color, 255-color, 0)
|
||||||
|
cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gfx_lores.disc(x, y as ubyte, radius, color)
|
||||||
|
circle_x[num_circles] = x
|
||||||
|
circle_y[num_circles] = y
|
||||||
|
circle_radius[num_circles] = radius
|
||||||
|
num_circles++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_circles
|
||||||
|
|
||||||
|
sub not_colliding() -> bool {
|
||||||
|
if num_circles==0
|
||||||
|
return true
|
||||||
|
ubyte @zp c
|
||||||
|
for c in 0 to num_circles-1 {
|
||||||
|
if distance(c) < (radius as uword) + circle_radius[c]
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub distance(ubyte cix) -> uword {
|
||||||
|
word dx = x as word - circle_x[cix]
|
||||||
|
word dy = y as word - circle_y[cix]
|
||||||
|
uword sqx = dx*dx as uword
|
||||||
|
uword sqy = dy*dy as uword
|
||||||
|
return sqrt(sqx + sqy)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub not_edge() -> bool {
|
||||||
|
if x as word - radius < 0
|
||||||
|
return false
|
||||||
|
if x + radius >= 320
|
||||||
|
return false
|
||||||
|
if y as word - radius < 0
|
||||||
|
return false
|
||||||
|
if y + radius >= 240
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
benchmark-program/b_life.p8
Normal file
123
benchmark-program/b_life.p8
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
; conway's game of life.
|
||||||
|
|
||||||
|
%import math
|
||||||
|
%import textio
|
||||||
|
|
||||||
|
life {
|
||||||
|
const ubyte WIDTH = 40
|
||||||
|
const ubyte HEIGHT = 30
|
||||||
|
const uword STRIDE = $0002+WIDTH
|
||||||
|
uword world1 = memory("world1", (WIDTH+2)*(HEIGHT+2), 0)
|
||||||
|
uword world2 = memory("world2", (WIDTH+2)*(HEIGHT+2), 0)
|
||||||
|
uword @requirezp active_world = world1
|
||||||
|
|
||||||
|
sub benchmark(uword max_time) -> uword {
|
||||||
|
txt.clear_screen()
|
||||||
|
sys.memset(world1, (WIDTH+2)*(HEIGHT+2), 0)
|
||||||
|
sys.memset(world2, (WIDTH+2)*(HEIGHT+2), 0)
|
||||||
|
|
||||||
|
set_start_gen()
|
||||||
|
|
||||||
|
uword gen
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
next_gen()
|
||||||
|
gen++
|
||||||
|
}
|
||||||
|
|
||||||
|
return gen
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_start_gen() {
|
||||||
|
|
||||||
|
; some way to set a custom start generation:
|
||||||
|
; str start_gen = " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " ** " +
|
||||||
|
; " * * " +
|
||||||
|
; " * " +
|
||||||
|
; " * * " +
|
||||||
|
; " ****** " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " " +
|
||||||
|
; " "
|
||||||
|
;
|
||||||
|
; for y in 0 to 15 {
|
||||||
|
; for x in 0 to 15 {
|
||||||
|
; if start_gen[y*16 + x]=='*'
|
||||||
|
; active_world[offset + x] = 1
|
||||||
|
; }
|
||||||
|
; offset += STRIDE
|
||||||
|
; }
|
||||||
|
|
||||||
|
; randomize whole world
|
||||||
|
math.rndseed(12345,9999)
|
||||||
|
uword offset = STRIDE+1
|
||||||
|
ubyte x
|
||||||
|
ubyte y
|
||||||
|
for y in 0 to HEIGHT-1 {
|
||||||
|
for x in 0 to WIDTH-1 {
|
||||||
|
active_world[offset+x] = math.rnd() & 1
|
||||||
|
}
|
||||||
|
offset += STRIDE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub next_gen() {
|
||||||
|
const ubyte DXOFFSET = 0
|
||||||
|
const ubyte DYOFFSET = 0
|
||||||
|
ubyte[2] cell_chars = [sc:' ', sc:'●']
|
||||||
|
|
||||||
|
uword @requirezp new_world = world1
|
||||||
|
if active_world == world1
|
||||||
|
new_world = world2
|
||||||
|
|
||||||
|
; To avoid re-calculating word index lookups into the new- and active world arrays,
|
||||||
|
; we calculate the required pointer values upfront.
|
||||||
|
; Inside the loop we can use ptr+x just fine (results in efficient LDA (ptr),Y instruction because x is a byte type),
|
||||||
|
; and for each row we simply add the stride to the pointer.
|
||||||
|
; It's more readable to use active_world[offset] etc, but offset is a word value, and this produces
|
||||||
|
; inefficient assembly code because we can't use a register indexed mode in this case. Costly inside a loop.
|
||||||
|
|
||||||
|
uword @requirezp new_world_ptr = new_world + STRIDE+1-DXOFFSET
|
||||||
|
uword @requirezp active_world_ptr = active_world + STRIDE+1-DXOFFSET
|
||||||
|
|
||||||
|
ubyte x
|
||||||
|
ubyte y
|
||||||
|
for y in DYOFFSET to HEIGHT+DYOFFSET-1 {
|
||||||
|
|
||||||
|
cx16.vaddr_autoincr(1, $b000 + 256*y, 0, 2) ; allows us to use simple Vera data byte assigns later instead of setchr() calls
|
||||||
|
|
||||||
|
for x in DXOFFSET to WIDTH+DXOFFSET-1 {
|
||||||
|
; count the living neighbors
|
||||||
|
ubyte cell = @(active_world_ptr + x)
|
||||||
|
uword @requirezp ptr = active_world_ptr + x - STRIDE - 1
|
||||||
|
ubyte neighbors = @(ptr) + @(ptr+1) + @(ptr+2) +
|
||||||
|
@(ptr+STRIDE) + cell + @(ptr+STRIDE+2) +
|
||||||
|
@(ptr+STRIDE*2) + @(ptr+STRIDE*2+1) + @(ptr+STRIDE*2+2)
|
||||||
|
|
||||||
|
; apply game of life rules
|
||||||
|
if neighbors==3
|
||||||
|
cell=1
|
||||||
|
else if neighbors!=4
|
||||||
|
cell=0
|
||||||
|
@(new_world_ptr + x) = cell
|
||||||
|
|
||||||
|
; draw new cell
|
||||||
|
; txt.setchr(x,y,cell_chars[cell])
|
||||||
|
cx16.VERA_DATA0 = cell_chars[cell]
|
||||||
|
}
|
||||||
|
active_world_ptr += STRIDE
|
||||||
|
new_world_ptr += STRIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
active_world = new_world
|
||||||
|
}
|
||||||
|
}
|
54
benchmark-program/b_mandelbrot.p8
Normal file
54
benchmark-program/b_mandelbrot.p8
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
%import textio
|
||||||
|
%import floats
|
||||||
|
|
||||||
|
mandelbrot {
|
||||||
|
const ubyte width = 39
|
||||||
|
const ubyte height = 29
|
||||||
|
const ubyte max_iter = 15
|
||||||
|
|
||||||
|
sub calc(uword max_time) -> uword {
|
||||||
|
uword num_pixels
|
||||||
|
ubyte pixelx
|
||||||
|
ubyte pixely
|
||||||
|
|
||||||
|
txt.home()
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
while cbm.RDTIM16() < max_time {
|
||||||
|
for pixely in 0 to height-1 {
|
||||||
|
float yy = (pixely as float)/0.40/height - 1.3
|
||||||
|
|
||||||
|
for pixelx in 0 to width-1 {
|
||||||
|
float xx = (pixelx as float)/0.32/width - 2.2
|
||||||
|
|
||||||
|
float xsquared = 0.0
|
||||||
|
float ysquared = 0.0
|
||||||
|
float x = 0.0
|
||||||
|
float y = 0.0
|
||||||
|
ubyte iter = 0
|
||||||
|
|
||||||
|
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||||
|
y = x*y*2.0 + yy
|
||||||
|
x = xsquared - ysquared + xx
|
||||||
|
xsquared = x*x
|
||||||
|
ysquared = y*y
|
||||||
|
iter++
|
||||||
|
}
|
||||||
|
txt.color2(1, max_iter-iter)
|
||||||
|
txt.spc()
|
||||||
|
num_pixels++
|
||||||
|
|
||||||
|
if cbm.RDTIM16()>=max_time
|
||||||
|
goto finished
|
||||||
|
}
|
||||||
|
txt.nl()
|
||||||
|
}
|
||||||
|
|
||||||
|
txt.clear_screen()
|
||||||
|
}
|
||||||
|
|
||||||
|
finished:
|
||||||
|
txt.color2(1, 6)
|
||||||
|
return num_pixels
|
||||||
|
}
|
||||||
|
}
|
343
benchmark-program/b_maze.p8
Normal file
343
benchmark-program/b_maze.p8
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
%import textio
|
||||||
|
%import math
|
||||||
|
|
||||||
|
; Even though prog8 only has support for extremely limited recursion,
|
||||||
|
; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure.
|
||||||
|
; This program shows a depth-first maze generation algorithm (1 possible path from start to finish),
|
||||||
|
; and a depth-first maze solver algorithm, both using a stack to store the path taken.
|
||||||
|
|
||||||
|
; Note: this program can be compiled for multiple target systems.
|
||||||
|
|
||||||
|
maze {
|
||||||
|
uword score
|
||||||
|
|
||||||
|
sub bench(uword max_time) -> uword {
|
||||||
|
txt.nl()
|
||||||
|
score=0
|
||||||
|
math.rndseed(2345,44332)
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
maze.initialize()
|
||||||
|
maze.drawStartFinish()
|
||||||
|
if maze.generate(max_time) {
|
||||||
|
maze.openpassages()
|
||||||
|
maze.drawStartFinish()
|
||||||
|
if maze.solve(max_time) {
|
||||||
|
maze.drawStartFinish()
|
||||||
|
} else break
|
||||||
|
} else break
|
||||||
|
}
|
||||||
|
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
const uword screenwidth = 40
|
||||||
|
const uword screenheight = 30
|
||||||
|
|
||||||
|
const ubyte numCellsHoriz = (screenwidth-1) / 2
|
||||||
|
const ubyte numCellsVert = (screenheight-1) / 2
|
||||||
|
|
||||||
|
; maze start and finish cells
|
||||||
|
const ubyte startCx = 0
|
||||||
|
const ubyte startCy = 0
|
||||||
|
const ubyte finishCx = numCellsHoriz-1
|
||||||
|
const ubyte finishCy = numCellsVert-1
|
||||||
|
|
||||||
|
; cell properties
|
||||||
|
const ubyte STONE = 128
|
||||||
|
const ubyte WALKED = 64
|
||||||
|
const ubyte BACKTRACKED = 32
|
||||||
|
const ubyte UP = 1
|
||||||
|
const ubyte RIGHT = 2
|
||||||
|
const ubyte DOWN = 4
|
||||||
|
const ubyte LEFT = 8
|
||||||
|
const ubyte WALLCOLOR = 12
|
||||||
|
const ubyte EMPTYCOLOR = 0
|
||||||
|
|
||||||
|
; unfortunately on larger screens (cx16), the number of cells exceeds 256 and doesn't fit in a regular array anymore.
|
||||||
|
uword cells = memory("cells", numCellsHoriz*numCellsVert, 0)
|
||||||
|
|
||||||
|
ubyte[256] cx_stack
|
||||||
|
ubyte[256] cy_stack
|
||||||
|
ubyte stackptr
|
||||||
|
|
||||||
|
ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN]
|
||||||
|
|
||||||
|
sub generate(uword max_time) -> bool {
|
||||||
|
ubyte cx = startCx
|
||||||
|
ubyte cy = startCy
|
||||||
|
|
||||||
|
stackptr = 0
|
||||||
|
@(celladdr(cx,cy)) &= ~STONE
|
||||||
|
drawCell(cx, cy)
|
||||||
|
uword cells_to_carve = numCellsHoriz * numCellsVert - 1
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
carve_restart_after_repath:
|
||||||
|
ubyte direction = choose_uncarved_direction()
|
||||||
|
if direction==0 {
|
||||||
|
;backtrack
|
||||||
|
stackptr--
|
||||||
|
if stackptr==255 {
|
||||||
|
; stack empty.
|
||||||
|
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
|
||||||
|
if cells_to_carve!=0 {
|
||||||
|
if repath()
|
||||||
|
goto carve_restart_after_repath
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
cx = cx_stack[stackptr]
|
||||||
|
cy = cy_stack[stackptr]
|
||||||
|
} else {
|
||||||
|
cx_stack[stackptr] = cx
|
||||||
|
cy_stack[stackptr] = cy
|
||||||
|
stackptr++
|
||||||
|
if stackptr==0 {
|
||||||
|
; stack overflow, we can't track our path any longer.
|
||||||
|
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
|
||||||
|
if cells_to_carve!=0 {
|
||||||
|
if repath()
|
||||||
|
goto carve_restart_after_repath
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@(celladdr(cx,cy)) |= direction
|
||||||
|
when direction {
|
||||||
|
UP -> {
|
||||||
|
cy--
|
||||||
|
@(celladdr(cx,cy)) |= DOWN
|
||||||
|
}
|
||||||
|
RIGHT -> {
|
||||||
|
cx++
|
||||||
|
@(celladdr(cx,cy)) |= LEFT
|
||||||
|
|
||||||
|
score++
|
||||||
|
}
|
||||||
|
DOWN -> {
|
||||||
|
cy++
|
||||||
|
@(celladdr(cx,cy)) |= UP
|
||||||
|
}
|
||||||
|
LEFT -> {
|
||||||
|
cx--
|
||||||
|
@(celladdr(cx,cy)) |= RIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@(celladdr(cx,cy)) &= ~STONE
|
||||||
|
cells_to_carve--
|
||||||
|
drawCell(cx, cy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
|
||||||
|
sub repath() -> bool {
|
||||||
|
; repath: try to find a new start cell with possible directions.
|
||||||
|
; we limit our number of searches so that the algorith doesn't get stuck
|
||||||
|
; for too long on bad rng... just accept a few unused cells in that case.
|
||||||
|
repeat 255 {
|
||||||
|
do {
|
||||||
|
cx = math.rnd() % numCellsHoriz
|
||||||
|
cy = math.rnd() % numCellsVert
|
||||||
|
} until @(celladdr(cx, cy)) & STONE ==0
|
||||||
|
if available_uncarved()!=0
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub available_uncarved() -> ubyte {
|
||||||
|
ubyte candidates = 0
|
||||||
|
if cx>0 and @(celladdr(cx-1, cy)) & STONE !=0
|
||||||
|
candidates |= LEFT
|
||||||
|
if cx<numCellsHoriz-1 and @(celladdr(cx+1, cy)) & STONE !=0
|
||||||
|
candidates |= RIGHT
|
||||||
|
if cy>0 and @(celladdr(cx, cy-1)) & STONE !=0
|
||||||
|
candidates |= UP
|
||||||
|
if cy<numCellsVert-1 and @(celladdr(cx, cy+1)) & STONE !=0
|
||||||
|
candidates |= DOWN
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
sub choose_uncarved_direction() -> ubyte {
|
||||||
|
ubyte candidates = available_uncarved()
|
||||||
|
if candidates==0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
ubyte choice = candidates & directionflags[math.rnd() & 3]
|
||||||
|
if choice!=0
|
||||||
|
return choice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub openpassages() {
|
||||||
|
; open just a few extra passages, so that multiple routes are possible in theory.
|
||||||
|
ubyte numpassages
|
||||||
|
ubyte cx
|
||||||
|
ubyte cy
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
cx = math.rnd() % (numCellsHoriz-2) + 1
|
||||||
|
cy = math.rnd() % (numCellsVert-2) + 1
|
||||||
|
} until @(celladdr(cx, cy)) & STONE ==0
|
||||||
|
ubyte direction = directionflags[math.rnd() & 3]
|
||||||
|
if @(celladdr(cx, cy)) & direction == 0 {
|
||||||
|
when direction {
|
||||||
|
LEFT -> {
|
||||||
|
if @(celladdr(cx-1,cy)) & STONE == 0 {
|
||||||
|
@(celladdr(cx,cy)) |= LEFT
|
||||||
|
drawCell(cx,cy)
|
||||||
|
numpassages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RIGHT -> {
|
||||||
|
if @(celladdr(cx+1,cy)) & STONE == 0 {
|
||||||
|
@(celladdr(cx,cy)) |= RIGHT
|
||||||
|
drawCell(cx,cy)
|
||||||
|
numpassages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UP -> {
|
||||||
|
if @(celladdr(cx,cy-1)) & STONE == 0 {
|
||||||
|
@(celladdr(cx,cy)) |= UP
|
||||||
|
drawCell(cx,cy)
|
||||||
|
numpassages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DOWN -> {
|
||||||
|
if @(celladdr(cx,cy+1)) & STONE == 0 {
|
||||||
|
@(celladdr(cx,cy)) |= DOWN
|
||||||
|
drawCell(cx,cy)
|
||||||
|
numpassages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} until numpassages==10
|
||||||
|
}
|
||||||
|
|
||||||
|
sub solve(uword max_time) -> bool {
|
||||||
|
ubyte cx = startCx
|
||||||
|
ubyte cy = startCy
|
||||||
|
const uword max_path_length = 1024
|
||||||
|
|
||||||
|
; the path through the maze can be longer than 256 so doesn't fit in a regular array.... :(
|
||||||
|
uword pathstack = memory("pathstack", max_path_length, 0)
|
||||||
|
uword pathstackptr = 0
|
||||||
|
|
||||||
|
@(celladdr(cx,cy)) |= WALKED
|
||||||
|
; txt.setcc(cx*2+1, cy*2+1, 81, 1)
|
||||||
|
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
solve_loop:
|
||||||
|
if cx==finishCx and cy==finishCy {
|
||||||
|
;txt.home()
|
||||||
|
txt.print("found! path length: ")
|
||||||
|
txt.print_uw(pathstackptr)
|
||||||
|
txt.nl()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte cell = @(celladdr(cx,cy))
|
||||||
|
if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 {
|
||||||
|
@(pathstack + pathstackptr) = UP
|
||||||
|
;txt.setcc(cx*2+1, cy*2, 81, 3)
|
||||||
|
cy--
|
||||||
|
}
|
||||||
|
else if cell & DOWN !=0 and @(celladdr(cx,cy+1)) & (WALKED|BACKTRACKED) ==0 {
|
||||||
|
@(pathstack + pathstackptr) = DOWN
|
||||||
|
;txt.setcc(cx*2+1, cy*2+2, 81, 3)
|
||||||
|
cy++
|
||||||
|
}
|
||||||
|
else if cell & LEFT !=0 and @(celladdr(cx-1,cy)) & (WALKED|BACKTRACKED) ==0 {
|
||||||
|
@(pathstack + pathstackptr) = LEFT
|
||||||
|
;txt.setcc(cx*2, cy*2+1, 81, 3)
|
||||||
|
cx--
|
||||||
|
}
|
||||||
|
else if cell & RIGHT !=0 and @(celladdr(cx+1,cy)) & (WALKED|BACKTRACKED) ==0 {
|
||||||
|
@(pathstack + pathstackptr) = RIGHT
|
||||||
|
;txt.setcc(cx*2+2, cy*2+1, 81, 3)
|
||||||
|
cx++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
; dead end, pop stack
|
||||||
|
pathstackptr--
|
||||||
|
if pathstackptr==65535 {
|
||||||
|
txt.print("no solution?!\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@(celladdr(cx,cy)) |= BACKTRACKED
|
||||||
|
;txt.setcc(cx*2+1, cy*2+1, 81, 2)
|
||||||
|
when @(pathstack + pathstackptr) {
|
||||||
|
UP -> {
|
||||||
|
;txt.setcc(cx*2+1, cy*2+2, 81, 9)
|
||||||
|
cy++
|
||||||
|
}
|
||||||
|
DOWN -> {
|
||||||
|
;txt.setcc(cx*2+1, cy*2, 81, 9)
|
||||||
|
cy--
|
||||||
|
}
|
||||||
|
LEFT -> {
|
||||||
|
;txt.setcc(cx*2+2, cy*2+1, 81, 9)
|
||||||
|
cx++
|
||||||
|
}
|
||||||
|
RIGHT -> {
|
||||||
|
;txt.setcc(cx*2, cy*2+1, 81, 9)
|
||||||
|
cx--
|
||||||
|
|
||||||
|
score++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto solve_loop
|
||||||
|
}
|
||||||
|
pathstackptr++
|
||||||
|
if pathstackptr==max_path_length {
|
||||||
|
txt.print("stack overflow, path too long\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@(celladdr(cx,cy)) |= WALKED
|
||||||
|
;txt.setcc(cx*2+1, cy*2+1, 81, 1)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub celladdr(ubyte cx, ubyte cy) -> uword {
|
||||||
|
return cells+(numCellsHoriz as uword)*cy+cx
|
||||||
|
}
|
||||||
|
|
||||||
|
sub drawCell(ubyte cx, ubyte cy) {
|
||||||
|
return
|
||||||
|
; ubyte x = cx * 2 + 1
|
||||||
|
; ubyte y = cy * 2 + 1
|
||||||
|
; ubyte doors = @(celladdr(cx,cy))
|
||||||
|
; if doors & UP !=0
|
||||||
|
; txt.setcc(x, y-1, ' ', EMPTYCOLOR)
|
||||||
|
; if doors & RIGHT !=0
|
||||||
|
; txt.setcc(x+1, y, ' ', EMPTYCOLOR)
|
||||||
|
; if doors & DOWN !=0
|
||||||
|
; txt.setcc(x, y+1, ' ', EMPTYCOLOR)
|
||||||
|
; if doors & LEFT !=0
|
||||||
|
; txt.setcc(x-1, y, ' ', EMPTYCOLOR)
|
||||||
|
; if doors & STONE !=0
|
||||||
|
; txt.setcc(x, y, 160, WALLCOLOR)
|
||||||
|
; else
|
||||||
|
; txt.setcc(x, y, 32, EMPTYCOLOR)
|
||||||
|
;
|
||||||
|
; if doors & WALKED !=0
|
||||||
|
; txt.setcc(x, y, 81, 1)
|
||||||
|
; if doors & BACKTRACKED !=0
|
||||||
|
; txt.setcc(x, y, 81, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub initialize() {
|
||||||
|
sys.memset(cells, numCellsHoriz*numCellsVert, STONE)
|
||||||
|
; txt.fill_screen(160, WALLCOLOR)
|
||||||
|
drawStartFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub drawStartFinish() {
|
||||||
|
; txt.setcc(startCx*2+1,startCy*2+1,sc:'s',5)
|
||||||
|
; txt.setcc(finishCx*2+1, finishCy*2+1, sc:'f', 13)
|
||||||
|
}
|
||||||
|
}
|
63
benchmark-program/b_queens.p8
Normal file
63
benchmark-program/b_queens.p8
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
%import textio
|
||||||
|
|
||||||
|
; Recursive N-Queens solver.
|
||||||
|
; The problem is: find all possible ways to place 8 Queen chess pieces on a chess board, so that none of them attacks any other.
|
||||||
|
; (this program prints all solutions without taking mirroring and flipping the chess board into account)
|
||||||
|
; Note: this program can be compiled for multiple target systems.
|
||||||
|
|
||||||
|
queens {
|
||||||
|
const ubyte NUMQUEENS=8
|
||||||
|
ubyte[NUMQUEENS] board
|
||||||
|
|
||||||
|
sub could_place(ubyte row, ubyte col) -> bool {
|
||||||
|
if row==0
|
||||||
|
return true
|
||||||
|
ubyte i
|
||||||
|
for i in 0 to row-1 {
|
||||||
|
if board[i]==col or board[i]-i==col-row or board[i]+i==col+row
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
uword solution_count
|
||||||
|
uword maximum_duration
|
||||||
|
|
||||||
|
sub place_queen(ubyte row) -> bool {
|
||||||
|
if row == NUMQUEENS {
|
||||||
|
solution_count++
|
||||||
|
txt.chrout('.')
|
||||||
|
return cbm.RDTIM16()<maximum_duration
|
||||||
|
}
|
||||||
|
bool continue_running=true
|
||||||
|
ubyte col
|
||||||
|
for col in 0 to NUMQUEENS-1 {
|
||||||
|
if could_place(row, col) {
|
||||||
|
board[row] = col
|
||||||
|
; we need to save the local variables row and col.
|
||||||
|
sys.push(row)
|
||||||
|
sys.push(col)
|
||||||
|
continue_running = place_queen(row + 1)
|
||||||
|
; restore the local variables after the recursive call.
|
||||||
|
col = sys.pop()
|
||||||
|
row = sys.pop()
|
||||||
|
board[row] = 0
|
||||||
|
|
||||||
|
if not continue_running
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return continue_running
|
||||||
|
}
|
||||||
|
|
||||||
|
sub bench(uword max_time) -> uword {
|
||||||
|
solution_count = 0
|
||||||
|
maximum_duration = max_time
|
||||||
|
txt.nl()
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
while cbm.RDTIM16() < maximum_duration {
|
||||||
|
void place_queen(0)
|
||||||
|
}
|
||||||
|
return solution_count
|
||||||
|
}
|
||||||
|
}
|
69
benchmark-program/b_sprites.p8
Normal file
69
benchmark-program/b_sprites.p8
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
%import sprites
|
||||||
|
%import coroutines
|
||||||
|
%import math
|
||||||
|
|
||||||
|
|
||||||
|
animsprites {
|
||||||
|
uword num_iterations
|
||||||
|
ubyte[64] sx
|
||||||
|
ubyte[64] sy
|
||||||
|
ubyte[64] sc
|
||||||
|
ubyte[64] dx
|
||||||
|
ubyte[64] dy
|
||||||
|
uword maximum_duration
|
||||||
|
|
||||||
|
sub benchmark(uword max_duration) -> uword {
|
||||||
|
maximum_duration = max_duration
|
||||||
|
math.rndseed(1122,9876)
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
cx16.mouse_config2(1)
|
||||||
|
sprites.set_mousepointer_hand()
|
||||||
|
repeat 64
|
||||||
|
void coroutines.add(animsprite, 0)
|
||||||
|
cx16.mouse_config2(0)
|
||||||
|
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
coroutines.run(supervisor)
|
||||||
|
|
||||||
|
sprites.reset(0, 64)
|
||||||
|
return num_iterations
|
||||||
|
}
|
||||||
|
|
||||||
|
sub supervisor() -> bool {
|
||||||
|
if cbm.RDTIM16() >= maximum_duration {
|
||||||
|
coroutines.killall()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub animsprite() {
|
||||||
|
num_iterations++
|
||||||
|
; set up the sprite
|
||||||
|
ubyte sprnum = coroutines.current()
|
||||||
|
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||||
|
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||||
|
sx[sprnum] = math.rnd()
|
||||||
|
sy[sprnum] = math.rnd()
|
||||||
|
sc[sprnum] = math.rnd()
|
||||||
|
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||||
|
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||||
|
|
||||||
|
; move the sprite around
|
||||||
|
while sc[sprnum]!=0 {
|
||||||
|
animate(sprnum)
|
||||||
|
void coroutines.yield()
|
||||||
|
sprnum = coroutines.current()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub animate(ubyte spr) {
|
||||||
|
defer sc[spr]--
|
||||||
|
sprites.pos(spr, sx[spr], sy[spr])
|
||||||
|
sx[spr] += dx[spr]
|
||||||
|
sy[spr] += dy[spr]
|
||||||
|
}
|
||||||
|
|
||||||
|
; end the task but replace it with a fresh animated sprite task
|
||||||
|
void coroutines.add(animsprite, 0)
|
||||||
|
}
|
||||||
|
}
|
989
benchmark-program/b_textelite.p8
Normal file
989
benchmark-program/b_textelite.p8
Normal file
@ -0,0 +1,989 @@
|
|||||||
|
%import textio
|
||||||
|
%import conv
|
||||||
|
%import strings
|
||||||
|
|
||||||
|
|
||||||
|
textelite {
|
||||||
|
|
||||||
|
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
|
||||||
|
const ubyte numforZaonce = 129
|
||||||
|
const ubyte numforDiso = 147
|
||||||
|
const ubyte numforRiedquat = 46
|
||||||
|
uword num_commands
|
||||||
|
|
||||||
|
sub bench(uword max_time) -> uword {
|
||||||
|
num_commands = 0
|
||||||
|
txt.lowercase()
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
while cbm.RDTIM16()<max_time {
|
||||||
|
reinit()
|
||||||
|
run_commands(max_time)
|
||||||
|
}
|
||||||
|
return num_commands
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reinit() {
|
||||||
|
;txt.clear_screen()
|
||||||
|
;txt.print("\n --- TextElite v1.3 ---\n")
|
||||||
|
txt.print("\nnew game\n")
|
||||||
|
elite_planet.set_seed(0, 0)
|
||||||
|
elite_galaxy.travel_to(1, numforLave)
|
||||||
|
elite_market.init(0) ; Lave's market is seeded with 0
|
||||||
|
elite_ship.init()
|
||||||
|
elite_planet.display(false, 0)
|
||||||
|
input_index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_commands(uword max_time) {
|
||||||
|
while cbm.RDTIM16() < max_time {
|
||||||
|
str input = "????????"
|
||||||
|
;txt.print("\nCash: ")
|
||||||
|
;elite_util.print_10s(elite_ship.cash)
|
||||||
|
;txt.print("\nCommand (?=help): ")
|
||||||
|
ubyte num_chars = next_input(input)
|
||||||
|
;txt.nl()
|
||||||
|
if num_chars!=0 {
|
||||||
|
when input[0] {
|
||||||
|
'q' -> {
|
||||||
|
bool has_error = false
|
||||||
|
if elite_galaxy.number != 2 {
|
||||||
|
txt.print("\nERROR: galaxy is not 2: ")
|
||||||
|
txt.print_ub(elite_galaxy.number)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_planet.number != 164 {
|
||||||
|
txt.print("\nERROR: planet is not 164: ")
|
||||||
|
txt.print_ub(elite_planet.number)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_planet.x != 116 {
|
||||||
|
txt.print("\nERROR: planet.x is not 116: ")
|
||||||
|
txt.print_ub(elite_planet.x)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_planet.y != 201 {
|
||||||
|
txt.print("\nERROR: planet.y is not 201: ")
|
||||||
|
txt.print_ub(elite_planet.y)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if "ribeen" != elite_planet.name {
|
||||||
|
txt.print("\nERROR: planet.name is not 'ribeen': ")
|
||||||
|
txt.print(elite_planet.name)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_ship.cash != 1212 {
|
||||||
|
txt.print("\nERROR: cash is not 1212: ")
|
||||||
|
txt.print_uw(elite_ship.cash)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_ship.fuel != 50 {
|
||||||
|
txt.print("\nERROR: fuel is not 50:")
|
||||||
|
txt.print_ub(elite_ship.fuel)
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_ship.cargohold[0] != 3 {
|
||||||
|
txt.print("\nERROR: food is not 3:")
|
||||||
|
txt.print_ub(elite_ship.cargohold[0])
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if elite_ship.cargohold[1] != 0 {
|
||||||
|
txt.print("\nERROR: textiles is not 0:")
|
||||||
|
txt.print_ub(elite_ship.cargohold[1])
|
||||||
|
txt.nl()
|
||||||
|
has_error=true
|
||||||
|
}
|
||||||
|
if has_error
|
||||||
|
sys.exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
'b' -> elite_trader.do_buy()
|
||||||
|
's' -> elite_trader.do_sell()
|
||||||
|
'f' -> elite_trader.do_fuel()
|
||||||
|
'j' -> elite_trader.do_jump()
|
||||||
|
't' -> elite_trader.do_teleport()
|
||||||
|
'g' -> elite_trader.do_next_galaxy()
|
||||||
|
'i' -> elite_trader.do_info()
|
||||||
|
'm' -> {
|
||||||
|
if input[1]=='a' and input[2]=='p'
|
||||||
|
elite_trader.do_map()
|
||||||
|
else
|
||||||
|
elite_trader.do_show_market()
|
||||||
|
}
|
||||||
|
'l' -> elite_trader.do_local()
|
||||||
|
'c' -> elite_trader.do_cash()
|
||||||
|
'h' -> elite_trader.do_hold()
|
||||||
|
}
|
||||||
|
num_commands++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str[] inputs = [
|
||||||
|
"i",
|
||||||
|
"diso",
|
||||||
|
"i",
|
||||||
|
"lave",
|
||||||
|
"m",
|
||||||
|
"b",
|
||||||
|
"food",
|
||||||
|
"15",
|
||||||
|
"map",
|
||||||
|
"g",
|
||||||
|
"map",
|
||||||
|
"l",
|
||||||
|
"j",
|
||||||
|
"zao",
|
||||||
|
"s",
|
||||||
|
"food",
|
||||||
|
"12",
|
||||||
|
"tele",
|
||||||
|
"quti",
|
||||||
|
"tele",
|
||||||
|
"aro",
|
||||||
|
"i",
|
||||||
|
"diso",
|
||||||
|
"i",
|
||||||
|
"lave",
|
||||||
|
"i",
|
||||||
|
"zao",
|
||||||
|
"galhyp",
|
||||||
|
"fuel",
|
||||||
|
"20",
|
||||||
|
"j",
|
||||||
|
"rib",
|
||||||
|
"i",
|
||||||
|
"rib",
|
||||||
|
"i",
|
||||||
|
"tiri",
|
||||||
|
"q",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
|
||||||
|
ubyte input_index
|
||||||
|
|
||||||
|
sub next_input(str buffer) -> ubyte {
|
||||||
|
input_index++
|
||||||
|
return strings.copy(inputs[input_index], buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_trader {
|
||||||
|
str input = "??????????"
|
||||||
|
ubyte num_chars
|
||||||
|
|
||||||
|
sub do_jump() {
|
||||||
|
;txt.print("\nJump to what system? ")
|
||||||
|
jump_to_system()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_teleport() {
|
||||||
|
;txt.print("\nCheat! Teleport to what system? ")
|
||||||
|
ubyte fuel = elite_ship.fuel
|
||||||
|
elite_ship.fuel = 255
|
||||||
|
jump_to_system()
|
||||||
|
elite_ship.fuel = fuel
|
||||||
|
}
|
||||||
|
|
||||||
|
sub jump_to_system() {
|
||||||
|
void textelite.next_input(input)
|
||||||
|
ubyte current_planet = elite_planet.number
|
||||||
|
ubyte x = elite_planet.x
|
||||||
|
ubyte y = elite_planet.y
|
||||||
|
if elite_galaxy.search_closest_planet(input) {
|
||||||
|
ubyte distance = elite_planet.distance(x, y)
|
||||||
|
if distance <= elite_ship.fuel {
|
||||||
|
elite_galaxy.init_market_for_planet()
|
||||||
|
elite_ship.fuel -= distance
|
||||||
|
;txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||||
|
elite_planet.display(true,0 )
|
||||||
|
return
|
||||||
|
}
|
||||||
|
;txt.print("\nInsufficient fuel\n")
|
||||||
|
} else {
|
||||||
|
;txt.print(" Not found!\n")
|
||||||
|
}
|
||||||
|
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_buy() {
|
||||||
|
;txt.print("\nBuy what commodity? ")
|
||||||
|
str commodity = "???????????????"
|
||||||
|
void textelite.next_input(commodity)
|
||||||
|
ubyte ci = elite_market.match(commodity)
|
||||||
|
if ci & 128 !=0 {
|
||||||
|
txt.print("Unknown\n")
|
||||||
|
} else {
|
||||||
|
;txt.print("\nHow much? ")
|
||||||
|
void textelite.next_input(input)
|
||||||
|
ubyte amount = conv.str2ubyte(input)
|
||||||
|
if elite_market.current_quantity[ci] < amount {
|
||||||
|
txt.print(" Insufficient supply!\n")
|
||||||
|
} else {
|
||||||
|
uword price = elite_market.current_price[ci] * amount
|
||||||
|
;txt.print(" Total price: ")
|
||||||
|
;elite_util.print_10s(price)
|
||||||
|
if price > elite_ship.cash {
|
||||||
|
txt.print(" Not enough cash!\n")
|
||||||
|
} else {
|
||||||
|
elite_ship.cash -= price
|
||||||
|
elite_ship.cargohold[ci] += amount
|
||||||
|
elite_market.current_quantity[ci] -= amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_sell() {
|
||||||
|
;txt.print("\nSell what commodity? ")
|
||||||
|
str commodity = "???????????????"
|
||||||
|
void textelite.next_input(commodity)
|
||||||
|
ubyte ci = elite_market.match(commodity)
|
||||||
|
if ci & 128 !=0 {
|
||||||
|
txt.print("Unknown\n")
|
||||||
|
} else {
|
||||||
|
;txt.print("\nHow much? ")
|
||||||
|
void textelite.next_input(input)
|
||||||
|
ubyte amount = conv.str2ubyte(input)
|
||||||
|
if elite_ship.cargohold[ci] < amount {
|
||||||
|
txt.print(" Insufficient supply!\n")
|
||||||
|
} else {
|
||||||
|
uword price = elite_market.current_price[ci] * amount
|
||||||
|
;txt.print(" Total price: ")
|
||||||
|
;elite_util.print_10s(price)
|
||||||
|
elite_ship.cash += price
|
||||||
|
elite_ship.cargohold[ci] -= amount
|
||||||
|
elite_market.current_quantity[ci] += amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_fuel() {
|
||||||
|
;txt.print("\nBuy fuel. Amount? ")
|
||||||
|
void textelite.next_input(input)
|
||||||
|
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||||
|
ubyte max_fuel = elite_ship.Max_fuel - elite_ship.fuel
|
||||||
|
if buy_fuel > max_fuel
|
||||||
|
buy_fuel = max_fuel
|
||||||
|
uword price = buy_fuel as uword * elite_ship.Fuel_cost
|
||||||
|
if price > elite_ship.cash {
|
||||||
|
txt.print("Not enough cash!\n")
|
||||||
|
} else {
|
||||||
|
elite_ship.cash -= price
|
||||||
|
elite_ship.fuel += buy_fuel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_cash() {
|
||||||
|
;txt.print("\nCheat! Set cash amount: ")
|
||||||
|
void textelite.next_input(input)
|
||||||
|
elite_ship.cash = conv.str2uword(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_hold() {
|
||||||
|
;txt.print("\nCheat! Set cargohold size: ")
|
||||||
|
void textelite.next_input(input)
|
||||||
|
elite_ship.Max_cargo = conv.str2ubyte(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_next_galaxy() {
|
||||||
|
txt.print("\n>>>>> Galaxy Hyperjump!\n")
|
||||||
|
elite_galaxy.travel_to(elite_galaxy.number+1, elite_planet.number)
|
||||||
|
elite_planet.display(false, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_info() {
|
||||||
|
;txt.print("\nSystem name (empty=current): ")
|
||||||
|
num_chars = textelite.next_input(input)
|
||||||
|
if num_chars!=0 {
|
||||||
|
ubyte current_planet = elite_planet.number
|
||||||
|
ubyte x = elite_planet.x
|
||||||
|
ubyte y = elite_planet.y
|
||||||
|
if elite_galaxy.search_closest_planet(input) {
|
||||||
|
ubyte distance = elite_planet.distance(x, y)
|
||||||
|
elite_planet.display(false, distance)
|
||||||
|
} else {
|
||||||
|
;txt.print(" Not found!")
|
||||||
|
}
|
||||||
|
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
|
||||||
|
} else {
|
||||||
|
elite_planet.display(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_local() {
|
||||||
|
elite_galaxy.local_area()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_map() {
|
||||||
|
;txt.print("\n(l)ocal or (g)alaxy starmap? ")
|
||||||
|
num_chars = textelite.next_input(input)
|
||||||
|
if num_chars!=0 {
|
||||||
|
elite_galaxy.starmap(input[0]=='l')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_market() {
|
||||||
|
elite_market.display()
|
||||||
|
;txt.print("\nFuel: ")
|
||||||
|
;elite_util.print_10s(elite_ship.fuel)
|
||||||
|
;txt.print(" Cargohold space: ")
|
||||||
|
;txt.print_ub(elite_ship.cargo_free())
|
||||||
|
;txt.print("t\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_ship {
|
||||||
|
const ubyte Max_fuel = 70
|
||||||
|
const ubyte Fuel_cost = 2
|
||||||
|
ubyte Max_cargo = 20
|
||||||
|
|
||||||
|
ubyte fuel
|
||||||
|
uword cash
|
||||||
|
ubyte[17] cargohold
|
||||||
|
|
||||||
|
sub init() {
|
||||||
|
sys.memset(cargohold, len(cargohold), 0)
|
||||||
|
fuel = Max_fuel
|
||||||
|
cash = 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_market {
|
||||||
|
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||||
|
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||||
|
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||||
|
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||||
|
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||||
|
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||||
|
|
||||||
|
ubyte[17] current_quantity
|
||||||
|
uword[17] current_price
|
||||||
|
|
||||||
|
sub init(ubyte fluct) {
|
||||||
|
; Prices and availabilities are influenced by the planet's economy type
|
||||||
|
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||||
|
; commander position to keep the market prices constant over gamesaves.
|
||||||
|
; Availabilities must be saved with the game since the player alters them
|
||||||
|
; by buying (and selling(?))
|
||||||
|
;
|
||||||
|
; Almost all commands are one byte only and overflow "errors" are
|
||||||
|
; extremely frequent and exploited.
|
||||||
|
;
|
||||||
|
; Trade Item prices are held internally in a single byte=true value/4.
|
||||||
|
; The decimal point in prices is introduced only when printing them.
|
||||||
|
; Internally, all prices are integers.
|
||||||
|
; The player's cash is held in four bytes.
|
||||||
|
ubyte ci
|
||||||
|
for ci in 0 to len(names)-1 {
|
||||||
|
word product
|
||||||
|
byte changing
|
||||||
|
product = elite_planet.economy as word * gradients[ci]
|
||||||
|
changing = fluct & maskbytes[ci] as byte
|
||||||
|
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||||
|
if q & $80 !=0
|
||||||
|
q = 0 ; clip to positive 8-bit
|
||||||
|
current_quantity[ci] = q & $3f
|
||||||
|
q = (baseprices[ci] + changing + product) as ubyte
|
||||||
|
current_price[ci] = q * $0004
|
||||||
|
}
|
||||||
|
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display() {
|
||||||
|
return
|
||||||
|
; ubyte ci
|
||||||
|
; txt.nl()
|
||||||
|
; elite_planet.print_name_uppercase()
|
||||||
|
; txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||||
|
; for ci in 0 to len(names)-1 {
|
||||||
|
; elite_util.print_right(13, names[ci])
|
||||||
|
; txt.print(" ")
|
||||||
|
; elite_util.print_10s(current_price[ci])
|
||||||
|
; txt.column(24)
|
||||||
|
; txt.print_ub(current_quantity[ci])
|
||||||
|
; txt.chrout(' ')
|
||||||
|
; when units[ci] {
|
||||||
|
; 0 -> txt.chrout('t')
|
||||||
|
; 1 -> txt.print("kg")
|
||||||
|
; 2 -> txt.chrout('g')
|
||||||
|
; }
|
||||||
|
; txt.column(32)
|
||||||
|
; txt.print_ub(elite_ship.cargohold[ci])
|
||||||
|
; txt.nl()
|
||||||
|
; }
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match(uword nameptr) -> ubyte {
|
||||||
|
ubyte ci
|
||||||
|
for ci in 0 to len(names)-1 {
|
||||||
|
if elite_util.prefix_matches(nameptr, names[ci])
|
||||||
|
return ci
|
||||||
|
}
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_galaxy {
|
||||||
|
const uword GALSIZE = 256
|
||||||
|
const uword base0 = $5A4A ; seeds for the first galaxy
|
||||||
|
const uword base1 = $0248
|
||||||
|
const uword base2 = $B753
|
||||||
|
|
||||||
|
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||||
|
|
||||||
|
ubyte number
|
||||||
|
|
||||||
|
uword[3] seed
|
||||||
|
|
||||||
|
sub init(ubyte galaxynum) {
|
||||||
|
number = 1
|
||||||
|
elite_planet.number = 255
|
||||||
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
|
repeat galaxynum-1 {
|
||||||
|
nextgalaxy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub nextgalaxy() {
|
||||||
|
textelite.num_commands++
|
||||||
|
|
||||||
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
|
number++
|
||||||
|
if number==9
|
||||||
|
number = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sub travel_to(ubyte galaxynum, ubyte system) {
|
||||||
|
init(galaxynum)
|
||||||
|
generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow)
|
||||||
|
repeat system {
|
||||||
|
generate_next_planet()
|
||||||
|
textelite.num_commands++
|
||||||
|
}
|
||||||
|
elite_planet.name = make_current_planet_name()
|
||||||
|
init_market_for_planet()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub init_market_for_planet() {
|
||||||
|
elite_market.init(lsb(seed[0])+msb(seed[2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
sub search_closest_planet(uword nameptr) -> bool {
|
||||||
|
textelite.num_commands++
|
||||||
|
|
||||||
|
ubyte x = elite_planet.x
|
||||||
|
ubyte y = elite_planet.y
|
||||||
|
ubyte current_planet_num = elite_planet.number
|
||||||
|
|
||||||
|
init(number)
|
||||||
|
bool found = false
|
||||||
|
ubyte current_closest_pi
|
||||||
|
ubyte current_distance = 127
|
||||||
|
ubyte pi
|
||||||
|
for pi in 0 to 255 {
|
||||||
|
generate_next_planet()
|
||||||
|
elite_planet.name = make_current_planet_name()
|
||||||
|
if elite_util.prefix_matches(nameptr, elite_planet.name) {
|
||||||
|
ubyte distance = elite_planet.distance(x, y)
|
||||||
|
if distance < current_distance {
|
||||||
|
current_distance = distance
|
||||||
|
current_closest_pi = pi
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found
|
||||||
|
travel_to(number, current_closest_pi)
|
||||||
|
else
|
||||||
|
travel_to(number, current_planet_num)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
sub local_area() {
|
||||||
|
ubyte current_planet = elite_planet.number
|
||||||
|
ubyte px = elite_planet.x
|
||||||
|
ubyte py = elite_planet.y
|
||||||
|
ubyte pn = 0
|
||||||
|
|
||||||
|
init(number)
|
||||||
|
; txt.print("\nGalaxy #")
|
||||||
|
; txt.print_ub(number)
|
||||||
|
; txt.print(" - systems in vicinity:\n")
|
||||||
|
do {
|
||||||
|
generate_next_planet()
|
||||||
|
ubyte distance = elite_planet.distance(px, py)
|
||||||
|
if distance <= elite_ship.Max_fuel {
|
||||||
|
; if distance <= elite_ship.fuel
|
||||||
|
; txt.chrout('*')
|
||||||
|
; else
|
||||||
|
; txt.chrout('-')
|
||||||
|
; txt.spc()
|
||||||
|
elite_planet.name = make_current_planet_name()
|
||||||
|
elite_planet.display(true, distance)
|
||||||
|
}
|
||||||
|
pn++
|
||||||
|
} until pn==0
|
||||||
|
|
||||||
|
travel_to(number, current_planet)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub starmap(bool local) {
|
||||||
|
ubyte current_planet = elite_planet.number
|
||||||
|
ubyte px = elite_planet.x
|
||||||
|
ubyte py = elite_planet.y
|
||||||
|
str current_name = " " ; 8 max
|
||||||
|
ubyte pn = 0
|
||||||
|
|
||||||
|
current_name = elite_planet.name
|
||||||
|
init(number)
|
||||||
|
; txt.clear_screen()
|
||||||
|
; txt.print("Galaxy #")
|
||||||
|
; txt.print_ub(number)
|
||||||
|
; if local
|
||||||
|
; txt.print(" - local systems")
|
||||||
|
; else
|
||||||
|
; txt.print(" - galaxy")
|
||||||
|
; txt.print(" starmap:\n")
|
||||||
|
ubyte max_distance = 255
|
||||||
|
if local
|
||||||
|
max_distance = elite_ship.Max_fuel
|
||||||
|
ubyte home_sx
|
||||||
|
ubyte home_sy
|
||||||
|
ubyte home_distance
|
||||||
|
|
||||||
|
do {
|
||||||
|
generate_next_planet()
|
||||||
|
ubyte distance = elite_planet.distance(px, py)
|
||||||
|
if distance <= max_distance {
|
||||||
|
elite_planet.name = make_current_planet_name()
|
||||||
|
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
|
||||||
|
uword tx = elite_planet.x
|
||||||
|
uword ty = elite_planet.y
|
||||||
|
if local {
|
||||||
|
tx = tx + 24 - px
|
||||||
|
ty = ty + 24 - py
|
||||||
|
}
|
||||||
|
ubyte sx = display_scale_x(tx)
|
||||||
|
ubyte sy = display_scale_y(ty)
|
||||||
|
ubyte char = '*'
|
||||||
|
if elite_planet.number==current_planet
|
||||||
|
char = '%'
|
||||||
|
if local {
|
||||||
|
print_planet_details(elite_planet.name, sx, sy, distance)
|
||||||
|
} else if elite_planet.number==current_planet {
|
||||||
|
home_distance = distance
|
||||||
|
home_sx = sx
|
||||||
|
home_sy = sy
|
||||||
|
}
|
||||||
|
; txt.setchr(2+sx, 2+sy, char)
|
||||||
|
}
|
||||||
|
pn++
|
||||||
|
} until pn==0
|
||||||
|
|
||||||
|
if not local
|
||||||
|
print_planet_details(current_name, home_sx, home_sy, home_distance)
|
||||||
|
|
||||||
|
; if local
|
||||||
|
; txt.plot(0, display_scale_y(64) + 4)
|
||||||
|
; else
|
||||||
|
; txt.plot(0, display_scale_y(256) + 4 as ubyte)
|
||||||
|
travel_to(number, current_planet)
|
||||||
|
|
||||||
|
sub print_planet_details(str name, ubyte screenx, ubyte screeny, ubyte d) {
|
||||||
|
return
|
||||||
|
; txt.plot(2+screenx-2, 2+screeny+1)
|
||||||
|
; txt.print(name)
|
||||||
|
; if d!=0 {
|
||||||
|
; txt.plot(2+screenx-2, 2+screeny+2)
|
||||||
|
; elite_util.print_10s(d)
|
||||||
|
; txt.print(" LY")
|
||||||
|
; }
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display_scale_x(uword x) -> ubyte {
|
||||||
|
if local
|
||||||
|
return x/2 as ubyte
|
||||||
|
return x/8 as ubyte
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display_scale_y(uword y) -> ubyte {
|
||||||
|
if local
|
||||||
|
return y/4 as ubyte
|
||||||
|
return y/16 as ubyte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte pn_pair1
|
||||||
|
ubyte pn_pair2
|
||||||
|
ubyte pn_pair3
|
||||||
|
ubyte pn_pair4
|
||||||
|
bool longname
|
||||||
|
|
||||||
|
sub generate_next_planet() {
|
||||||
|
determine_planet_properties()
|
||||||
|
longname = lsb(seed[0]) & 64 !=0
|
||||||
|
|
||||||
|
; Always four iterations of random number
|
||||||
|
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub make_current_planet_name() -> str {
|
||||||
|
ubyte ni = 0
|
||||||
|
str name = " " ; max 8
|
||||||
|
|
||||||
|
if pn_pairs[pn_pair1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair1+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair1+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair2] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair2]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair2+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair2+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair3] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair3]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair3+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair3+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
|
||||||
|
if longname {
|
||||||
|
if pn_pairs[pn_pair4] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair4]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair4+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair4+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name[ni] = 0
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
sub determine_planet_properties() {
|
||||||
|
; create the planet's characteristics
|
||||||
|
elite_planet.number++
|
||||||
|
elite_planet.x = msb(seed[1])
|
||||||
|
elite_planet.y = msb(seed[0])
|
||||||
|
elite_planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||||
|
elite_planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||||
|
if elite_planet.govtype <= 1
|
||||||
|
elite_planet.economy = (elite_planet.economy | 2)
|
||||||
|
elite_planet.techlevel = (msb(seed[1]) & 3) + (elite_planet.economy ^ 7)
|
||||||
|
elite_planet.techlevel += elite_planet.govtype >> 1
|
||||||
|
if elite_planet.govtype & 1 !=0
|
||||||
|
elite_planet.techlevel++
|
||||||
|
elite_planet.population = 4 * elite_planet.techlevel + elite_planet.economy
|
||||||
|
elite_planet.population += elite_planet.govtype + 1
|
||||||
|
elite_planet.productivity = ((elite_planet.economy ^ 7) + 3) * (elite_planet.govtype + 4)
|
||||||
|
elite_planet.productivity *= elite_planet.population * 8
|
||||||
|
ubyte seed2_msb = msb(seed[2])
|
||||||
|
elite_planet.radius = mkword((seed2_msb & 15) + 11, elite_planet.x)
|
||||||
|
elite_planet.species_is_alien = lsb(seed[2]) & 128 !=0 ; bit 7 of w2_lo
|
||||||
|
if elite_planet.species_is_alien {
|
||||||
|
elite_planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||||
|
elite_planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||||
|
elite_planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
|
elite_planet.species_kind = (elite_planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_planet.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
elite_planet.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
elite_planet.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
elite_planet.goatsoup_seed[3] = seed2_msb
|
||||||
|
}
|
||||||
|
|
||||||
|
sub tweakseed() {
|
||||||
|
uword temp = seed[0] + seed[1] + seed[2]
|
||||||
|
seed[0] = seed[1]
|
||||||
|
seed[1] = seed[2]
|
||||||
|
seed[2] = temp
|
||||||
|
}
|
||||||
|
|
||||||
|
sub twist(uword x) -> uword {
|
||||||
|
ubyte xh = msb(x)
|
||||||
|
ubyte xl = lsb(x)
|
||||||
|
xh <<= 1 ; make sure carry flag is not used on first shift!
|
||||||
|
rol(xl)
|
||||||
|
return mkword(xh, xl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_planet {
|
||||||
|
str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||||
|
str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||||
|
str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||||
|
str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||||
|
str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||||
|
str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||||
|
str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||||
|
str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||||
|
str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||||
|
str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||||
|
str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||||
|
str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||||
|
str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||||
|
str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||||
|
str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
|
||||||
|
str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||||
|
str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"]
|
||||||
|
str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||||
|
str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||||
|
str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||||
|
str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||||
|
str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||||
|
str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||||
|
str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||||
|
str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||||
|
str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||||
|
str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||||
|
str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||||
|
str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||||
|
str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||||
|
str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||||
|
str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||||
|
str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||||
|
str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||||
|
str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||||
|
str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||||
|
|
||||||
|
uword[] @shared wordlists = [
|
||||||
|
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||||
|
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||||
|
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||||
|
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||||
|
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||||
|
|
||||||
|
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||||
|
|
||||||
|
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||||
|
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||||
|
|
||||||
|
str name = " " ; 8 max
|
||||||
|
ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet
|
||||||
|
ubyte x
|
||||||
|
ubyte y
|
||||||
|
ubyte economy
|
||||||
|
ubyte govtype
|
||||||
|
ubyte techlevel
|
||||||
|
ubyte population
|
||||||
|
uword productivity
|
||||||
|
uword radius
|
||||||
|
bool species_is_alien ; otherwise "Human Colonials"
|
||||||
|
ubyte species_size
|
||||||
|
ubyte species_color
|
||||||
|
ubyte species_look
|
||||||
|
ubyte species_kind
|
||||||
|
|
||||||
|
sub set_seed(uword s1, uword s2) {
|
||||||
|
goatsoup_seed[0] = lsb(s1)
|
||||||
|
goatsoup_seed[1] = msb(s1)
|
||||||
|
goatsoup_seed[2] = lsb(s2)
|
||||||
|
goatsoup_seed[3] = msb(s2)
|
||||||
|
reset_rnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reset_rnd() {
|
||||||
|
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||||
|
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||||
|
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||||
|
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
sub random_name() -> str {
|
||||||
|
ubyte ii
|
||||||
|
str randname = " " ; 8 chars max
|
||||||
|
ubyte nx = 0
|
||||||
|
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||||
|
ubyte xx = goatsoup_rnd_number() & $3e
|
||||||
|
if pairs0[xx] != '.' {
|
||||||
|
randname[nx] = pairs0[xx]
|
||||||
|
nx++
|
||||||
|
}
|
||||||
|
xx++
|
||||||
|
if pairs0[xx] != '.' {
|
||||||
|
randname[nx] = pairs0[xx]
|
||||||
|
nx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
randname[nx] = 0
|
||||||
|
randname[0] = strings.upperchar(randname[0])
|
||||||
|
return randname
|
||||||
|
}
|
||||||
|
|
||||||
|
sub goatsoup_rnd_number() -> ubyte {
|
||||||
|
ubyte xx = goatsoup_rnd[0] * 2
|
||||||
|
uword a = xx as uword + goatsoup_rnd[2]
|
||||||
|
if goatsoup_rnd[0] > 127
|
||||||
|
a ++
|
||||||
|
goatsoup_rnd[0] = lsb(a)
|
||||||
|
goatsoup_rnd[2] = xx
|
||||||
|
xx = goatsoup_rnd[1]
|
||||||
|
ubyte ac = xx + goatsoup_rnd[3] + msb(a)
|
||||||
|
goatsoup_rnd[1] = ac
|
||||||
|
goatsoup_rnd[3] = xx
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||||
|
uword ax
|
||||||
|
uword ay
|
||||||
|
if px>x
|
||||||
|
ax=px-x
|
||||||
|
else
|
||||||
|
ax=x-px
|
||||||
|
if py>y
|
||||||
|
ay=py-y
|
||||||
|
else
|
||||||
|
ay=y-py
|
||||||
|
ay /= 2
|
||||||
|
ubyte d = sqrt(ax*ax + ay*ay)
|
||||||
|
if d>63
|
||||||
|
return 255
|
||||||
|
return d*4
|
||||||
|
}
|
||||||
|
|
||||||
|
sub soup() -> str {
|
||||||
|
str planet_result = " " * 160
|
||||||
|
uword[6] source_stack
|
||||||
|
ubyte stack_ptr = 0
|
||||||
|
str start_source = "\x8F is \x97."
|
||||||
|
uword source_ptr = &start_source
|
||||||
|
uword result_ptr = &planet_result
|
||||||
|
|
||||||
|
reset_rnd()
|
||||||
|
recursive_soup()
|
||||||
|
return planet_result
|
||||||
|
|
||||||
|
sub recursive_soup() {
|
||||||
|
repeat {
|
||||||
|
ubyte c = @(source_ptr)
|
||||||
|
source_ptr++
|
||||||
|
if c == $00 {
|
||||||
|
@(result_ptr) = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if c <= $80 {
|
||||||
|
@(result_ptr) = c
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if c <= $a4 {
|
||||||
|
ubyte rnr = goatsoup_rnd_number()
|
||||||
|
ubyte wordNr = ((rnr >= $33) as ubyte) + ((rnr >= $66) as ubyte) + ((rnr >= $99) as ubyte) + ((rnr >= $CC) as ubyte)
|
||||||
|
source_stack[stack_ptr] = source_ptr
|
||||||
|
stack_ptr++
|
||||||
|
source_ptr = getword(c, wordNr)
|
||||||
|
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||||
|
stack_ptr--
|
||||||
|
source_ptr = source_stack[stack_ptr]
|
||||||
|
} else {
|
||||||
|
if c == $b0 {
|
||||||
|
@(result_ptr) = strings.upperchar(name[0])
|
||||||
|
result_ptr++
|
||||||
|
concat_string(&name + 1)
|
||||||
|
}
|
||||||
|
else if c == $b1 {
|
||||||
|
@(result_ptr) = strings.upperchar(name[0])
|
||||||
|
result_ptr++
|
||||||
|
ubyte ni
|
||||||
|
for ni in 1 to len(name) {
|
||||||
|
ubyte cc = name[ni]
|
||||||
|
if cc in ['e', 'o', 0]
|
||||||
|
break
|
||||||
|
else {
|
||||||
|
@(result_ptr) = cc
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@(result_ptr) = 'i'
|
||||||
|
result_ptr++
|
||||||
|
@(result_ptr) = 'a'
|
||||||
|
result_ptr++
|
||||||
|
@(result_ptr) = 'n'
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
else if c == $b2 {
|
||||||
|
concat_string(random_name())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
@(result_ptr) = c
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub concat_string(uword str_ptr) {
|
||||||
|
repeat {
|
||||||
|
ubyte c = @(str_ptr)
|
||||||
|
if c==0
|
||||||
|
break
|
||||||
|
else {
|
||||||
|
@(result_ptr) = c
|
||||||
|
str_ptr++
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display(bool compressed, ubyte distance) {
|
||||||
|
txt.print(soup())
|
||||||
|
txt.nl()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub getword(ubyte listnum, ubyte wordidx) -> uword {
|
||||||
|
uword list = wordlists[listnum-$81]
|
||||||
|
return peekw(list + wordidx*2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elite_util {
|
||||||
|
sub prefix_matches(uword prefixptr, uword stringptr) -> bool {
|
||||||
|
repeat {
|
||||||
|
ubyte pc = @(prefixptr)
|
||||||
|
ubyte sc = @(stringptr)
|
||||||
|
if pc == 0
|
||||||
|
return true
|
||||||
|
; to lowercase for case insensitive compare:
|
||||||
|
if strings.lowerchar(pc)!=strings.lowerchar(sc)
|
||||||
|
return false
|
||||||
|
prefixptr++
|
||||||
|
stringptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
benchmark-program/benchmark.p8
Normal file
117
benchmark-program/benchmark.p8
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
|
||||||
|
; This benchmark program is meant to check for regressions in the
|
||||||
|
; Prog8 compiler's code-generator (performance wise).
|
||||||
|
;
|
||||||
|
; As the X16 computer is a more or less fixed system, it's not very useful
|
||||||
|
; to benchmark the computer itself with.
|
||||||
|
|
||||||
|
|
||||||
|
%import textio
|
||||||
|
%import b_adpcm
|
||||||
|
%import b_circles
|
||||||
|
%import b_3d
|
||||||
|
%import b_life
|
||||||
|
%import b_mandelbrot
|
||||||
|
%import b_queens
|
||||||
|
%import b_textelite
|
||||||
|
%import b_maze
|
||||||
|
%import b_sprites
|
||||||
|
|
||||||
|
%zeropage basicsafe
|
||||||
|
%option no_sysinit
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
str[20] benchmark_names
|
||||||
|
uword[20] benchmark_score
|
||||||
|
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubyte benchmark_number
|
||||||
|
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
txt.color2(1, 6)
|
||||||
|
txt.clear_screen()
|
||||||
|
|
||||||
|
txt.print("\n\n\n prog8 compiler benchmark tests.\n")
|
||||||
|
sys.wait(60)
|
||||||
|
|
||||||
|
benchmark_number = 0
|
||||||
|
|
||||||
|
announce_benchmark("maze solver")
|
||||||
|
benchmark_score[benchmark_number] = maze.bench(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("n-queens")
|
||||||
|
benchmark_score[benchmark_number] = queens.bench(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("mandelbrot (floating point)")
|
||||||
|
benchmark_score[benchmark_number] = mandelbrot.calc(400)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("game of life")
|
||||||
|
benchmark_score[benchmark_number] = life.benchmark(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("3d model rotation")
|
||||||
|
benchmark_score[benchmark_number] = rotate3d.benchmark(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("adpcm audio decoding")
|
||||||
|
benchmark_score[benchmark_number] = adpcm.decode_benchmark(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("circles with gfx_lores")
|
||||||
|
benchmark_score[benchmark_number] = circles.draw(false, 300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
; announce_benchmark("circles with kernal")
|
||||||
|
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||||
|
; benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("text-elite")
|
||||||
|
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
announce_benchmark("sprites-coroutines-defer")
|
||||||
|
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||||
|
benchmark_number++
|
||||||
|
|
||||||
|
benchmark_names[benchmark_number] = 0
|
||||||
|
benchmark_score[benchmark_number] = 0
|
||||||
|
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
txt.uppercase()
|
||||||
|
txt.color2(1, 6)
|
||||||
|
uword final_score
|
||||||
|
benchmark_number = 0
|
||||||
|
txt.print("\nscore benchmark\n\n")
|
||||||
|
do {
|
||||||
|
txt.spc()
|
||||||
|
txt.print_uw(benchmark_score[benchmark_number])
|
||||||
|
txt.column(6)
|
||||||
|
txt.print(benchmark_names[benchmark_number])
|
||||||
|
final_score += benchmark_score[benchmark_number]
|
||||||
|
txt.nl()
|
||||||
|
benchmark_number++
|
||||||
|
} until benchmark_names[benchmark_number]==0
|
||||||
|
|
||||||
|
txt.print("\n\nfinal score : ")
|
||||||
|
txt.print_uw(final_score)
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
sub announce_benchmark(str name) {
|
||||||
|
benchmark_names[benchmark_number] = name
|
||||||
|
cx16.set_screen_mode(3)
|
||||||
|
txt.uppercase()
|
||||||
|
txt.color2(1, 6)
|
||||||
|
txt.clear_screen()
|
||||||
|
txt.plot(4, 6)
|
||||||
|
txt.print(benchmark_names[benchmark_number])
|
||||||
|
txt.nl()
|
||||||
|
sys.wait(60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
build.gradle
10
build.gradle
@ -1,10 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
34
build.gradle.kts
Normal file
34
build.gradle.kts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||||
|
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "2.1.20"
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply(plugin="kotlin")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||||
|
jvmTarget = JvmTarget.JVM_11
|
||||||
|
}
|
||||||
|
sourceSets.all {
|
||||||
|
languageSettings {
|
||||||
|
// enable language features like so:
|
||||||
|
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
id "org.jetbrains.kotlin.jvm"
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// should have no dependencies to other modules
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
java {
|
|
||||||
srcDir "${project.projectDir}/src"
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
srcDir "${project.projectDir}/res"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
|
24
codeCore/build.gradle.kts
Normal file
24
codeCore/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// should have no dependencies to other modules
|
||||||
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir("${project.projectDir}/src")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDir("${project.projectDir}/res")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
31
codeCore/src/prog8/code/Globals.kt
Normal file
31
codeCore/src/prog8/code/Globals.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package prog8.code
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
|
||||||
|
|
||||||
|
// the automatically generated module where all string literals are interned to:
|
||||||
|
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||||
|
|
||||||
|
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||||
|
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute path of the given path,
|
||||||
|
* where links are replaced by the actual directories,
|
||||||
|
* and containing no redundant path elements.
|
||||||
|
* If the path doesn't refer to an existing directory or file on the file system,
|
||||||
|
* it is returned unchanged.
|
||||||
|
*/
|
||||||
|
fun Path.sanitize(): Path {
|
||||||
|
return try {
|
||||||
|
this.toRealPath().normalize()
|
||||||
|
} catch (_: java.nio.file.NoSuchFileException) {
|
||||||
|
this.absolute().normalize()
|
||||||
|
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||||
|
} catch (iox: IOException) {
|
||||||
|
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||||
|
}
|
||||||
|
}
|
@ -1,181 +0,0 @@
|
|||||||
package prog8.code.ast
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
|
|
||||||
|
|
||||||
sealed interface IPtSubroutine {
|
|
||||||
val name: String
|
|
||||||
val scopedName: String
|
|
||||||
}
|
|
||||||
|
|
||||||
class PtAsmSub(
|
|
||||||
name: String,
|
|
||||||
val address: UInt?,
|
|
||||||
val clobbers: Set<CpuRegister>,
|
|
||||||
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
|
||||||
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
|
||||||
val inline: Boolean,
|
|
||||||
position: Position
|
|
||||||
) : PtNamedNode(name, position), IPtSubroutine
|
|
||||||
|
|
||||||
|
|
||||||
class PtSub(
|
|
||||||
name: String,
|
|
||||||
val parameters: List<PtSubroutineParameter>,
|
|
||||||
val returntype: DataType?,
|
|
||||||
position: Position
|
|
||||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
|
||||||
init {
|
|
||||||
// params and return value should not be str
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
|
||||||
|
|
||||||
|
|
||||||
sealed interface IPtAssignment {
|
|
||||||
val children: MutableList<PtNode>
|
|
||||||
val target: 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.last() as PtExpression
|
|
||||||
val multiTarget: Boolean
|
|
||||||
get() = children.size>2
|
|
||||||
}
|
|
||||||
|
|
||||||
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
|
||||||
|
|
||||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
|
||||||
|
|
||||||
|
|
||||||
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
|
|
||||||
val identifier: PtIdentifier?
|
|
||||||
get() = children.single() as? PtIdentifier
|
|
||||||
val array: PtArrayIndexer?
|
|
||||||
get() = children.single() as? PtArrayIndexer
|
|
||||||
val memory: PtMemoryByte?
|
|
||||||
get() = children.single() as? PtMemoryByte
|
|
||||||
|
|
||||||
val type: DataType
|
|
||||||
get() {
|
|
||||||
return when(val tgt = children.single()) {
|
|
||||||
is PtIdentifier -> tgt.type
|
|
||||||
is PtArrayIndexer -> tgt.type
|
|
||||||
is PtMemoryByte -> tgt.type
|
|
||||||
else -> throw AssemblyError("weird target $tgt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
|
|
||||||
val trueScope: PtNodeGroup
|
|
||||||
get() = children[0] as PtNodeGroup
|
|
||||||
val falseScope: PtNodeGroup
|
|
||||||
get() = children[1] as PtNodeGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtForLoop(position: Position) : PtNode(position) {
|
|
||||||
val variable: PtIdentifier
|
|
||||||
get() = children[0] as PtIdentifier
|
|
||||||
val iterable: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
val statements: PtNodeGroup
|
|
||||||
get() = children[2] as PtNodeGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtIfElse(position: Position) : PtNode(position) {
|
|
||||||
val condition: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val ifScope: PtNodeGroup
|
|
||||||
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?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
|
|
||||||
val address: UInt?,
|
|
||||||
position: Position) : PtNode(position) {
|
|
||||||
init {
|
|
||||||
identifier?.let {it.parent = this }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
|
||||||
val count: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val statements: PtNodeGroup
|
|
||||||
get() = children[1] as PtNodeGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtReturn(position: Position) : PtNode(position) {
|
|
||||||
val hasValue = children.any()
|
|
||||||
val value: PtExpression?
|
|
||||||
get() {
|
|
||||||
return if(children.any())
|
|
||||||
children.single() as PtExpression
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sealed interface IPtVariable {
|
|
||||||
val name: String
|
|
||||||
val type: DataType
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
|
||||||
init {
|
|
||||||
value?.let {it.parent=this}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 {
|
|
||||||
init {
|
|
||||||
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtWhen(position: Position) : PtNode(position) {
|
|
||||||
val value: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val choices: PtNodeGroup
|
|
||||||
get() = children[1] as PtNodeGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
|
||||||
val values: PtNodeGroup
|
|
||||||
get() = children[0] as PtNodeGroup
|
|
||||||
val statements: PtNodeGroup
|
|
||||||
get() = children[1] as PtNodeGroup
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
|
||||||
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
|
class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
|
||||||
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val paramConvs = params.mapIndexed { index, it ->
|
val paramConvs = params.mapIndexed { index, it ->
|
||||||
@ -13,35 +13,41 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve
|
|||||||
}
|
}
|
||||||
val returnConv =
|
val returnConv =
|
||||||
when {
|
when {
|
||||||
returns.reg!=null -> returns.reg.toString()
|
returns.reg == RegisterOrPair.FAC1 -> "floatFAC1"
|
||||||
returns.floatFac1 -> "floatFAC1"
|
returns.reg != null -> returns.reg.toString()
|
||||||
else -> "<no returnvalue>"
|
else -> "<no returnvalue>"
|
||||||
}
|
}
|
||||||
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
|
||||||
|
|
||||||
|
|
||||||
|
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||||
|
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||||
|
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
|
||||||
|
|
||||||
class FSignature(val pure: Boolean, // does it have side effects?
|
class FSignature(val pure: Boolean, // does it have side effects?
|
||||||
val parameters: List<FParam>,
|
val returnType: BaseDataType?,
|
||||||
val returnType: DataType?) {
|
vararg val parameters: FParam) {
|
||||||
|
|
||||||
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
|
||||||
val returns: ReturnConvention = when (returnType) {
|
val returns: ReturnConvention = when (returnType) {
|
||||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
|
||||||
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||||
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||||
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
||||||
null -> ReturnConvention(null, null, false)
|
null -> ReturnConvention(null, null)
|
||||||
else -> {
|
else -> {
|
||||||
// return type depends on arg type
|
// return type depends on arg type
|
||||||
when (val paramType = actualParamTypes.first()) {
|
when (val paramType = actualParamTypes.first()) {
|
||||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
|
||||||
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||||
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||||
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||||
else -> ReturnConvention(paramType, null, false)
|
else -> ReturnConvention(paramType, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,16 +55,27 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
|||||||
return when {
|
return when {
|
||||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||||
actualParamTypes.size==1 -> {
|
actualParamTypes.size==1 -> {
|
||||||
// one parameter goes via register/registerpair
|
// One parameter goes via register/registerpair.
|
||||||
|
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
|
||||||
|
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
|
||||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||||
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||||
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
|
||||||
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
else -> ParamConvention(paramType, null, false)
|
else -> ParamConvention(paramType, null, false)
|
||||||
}
|
}
|
||||||
CallConvention(listOf(paramConv), returns)
|
CallConvention(listOf(paramConv), returns)
|
||||||
}
|
}
|
||||||
|
actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> {
|
||||||
|
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
|
||||||
|
}
|
||||||
|
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
|
||||||
|
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
|
||||||
|
}
|
||||||
|
actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> {
|
||||||
|
TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet")
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// multiple parameters go via variables
|
// multiple parameters go via variables
|
||||||
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||||
@ -69,66 +86,63 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
|||||||
}
|
}
|
||||||
|
|
||||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||||
// this set of function have no return value and operate in-place:
|
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
|
||||||
// cmp returns a status in the carry flag, but not a proper return value
|
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
|
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||||
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
|
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||||
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
|
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||||
"prog8_ifelse_bittest_notset" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
|
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||||
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
|
||||||
// normal functions follow:
|
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
|
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
|
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
|
||||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
|
||||||
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
"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),
|
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
"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),
|
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||||
"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),
|
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||||
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
|
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||||
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
|
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
|
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||||
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
|
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||||
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||||
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||||
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||||
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||||
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||||
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||||
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||||
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||||
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||||
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
|
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
"rsave" to FSignature(false, null),
|
||||||
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
"rrestore" to FSignature(false, null),
|
||||||
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
|
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
|
||||||
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
|
||||||
"rsave" to FSignature(false, emptyList(), null),
|
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
|
||||||
"rrestore" to FSignature(false, emptyList(), null),
|
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||||
"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(
|
val InplaceModifyingBuiltinFunctions = setOf(
|
||||||
|
@ -11,9 +11,11 @@ class CompilationOptions(val output: OutputType,
|
|||||||
val zpAllowed: List<UIntRange>,
|
val zpAllowed: List<UIntRange>,
|
||||||
val floats: Boolean,
|
val floats: Boolean,
|
||||||
val noSysInit: Boolean,
|
val noSysInit: Boolean,
|
||||||
|
val romable: Boolean,
|
||||||
val compTarget: ICompilationTarget,
|
val compTarget: ICompilationTarget,
|
||||||
// these are set later, based on command line arguments or options in the source code:
|
// these are set later, based on command line arguments or options in the source code:
|
||||||
var loadAddress: UInt,
|
var loadAddress: UInt,
|
||||||
|
var memtopAddress: UInt,
|
||||||
var warnSymbolShadowing: Boolean = false,
|
var warnSymbolShadowing: Boolean = false,
|
||||||
var optimize: Boolean = false,
|
var optimize: Boolean = false,
|
||||||
var asmQuiet: Boolean = false,
|
var asmQuiet: Boolean = false,
|
||||||
@ -26,13 +28,15 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var varsGolden: Boolean = false,
|
var varsGolden: Boolean = false,
|
||||||
var slabsHighBank: Int? = null,
|
var slabsHighBank: Int? = null,
|
||||||
var slabsGolden: Boolean = false,
|
var slabsGolden: Boolean = false,
|
||||||
var splitWordArrays: Boolean = false,
|
var dontSplitWordArrays: Boolean = false,
|
||||||
var breakpointCpuInstruction: String? = null,
|
var breakpointCpuInstruction: String? = null,
|
||||||
|
var ignoreFootguns: Boolean = false,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
|
var quiet: Boolean = false,
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
compTarget.machine.initializeMemoryAreas(this)
|
compTarget.initializeMemoryAreas(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -11,6 +11,7 @@ fun Number.toHex(): String {
|
|||||||
// 0..15 -> "0".."15"
|
// 0..15 -> "0".."15"
|
||||||
// 16..255 -> "$10".."$ff"
|
// 16..255 -> "$10".."$ff"
|
||||||
// 256..65536 -> "$0100".."$ffff"
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
// larger -> "$12345678"
|
||||||
// negative values are prefixed with '-'.
|
// negative values are prefixed with '-'.
|
||||||
val integer = this.toInt()
|
val integer = this.toInt()
|
||||||
if(integer<0)
|
if(integer<0)
|
||||||
@ -19,7 +20,7 @@ fun Number.toHex(): String {
|
|||||||
in 0 until 16 -> integer.toString()
|
in 0 until 16 -> integer.toString()
|
||||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||||
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
||||||
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
else -> "$"+integer.toString(16).padStart(8,'0')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,11 +28,12 @@ fun UInt.toHex(): String {
|
|||||||
// 0..15 -> "0".."15"
|
// 0..15 -> "0".."15"
|
||||||
// 16..255 -> "$10".."$ff"
|
// 16..255 -> "$10".."$ff"
|
||||||
// 256..65536 -> "$0100".."$ffff"
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
// larger -> "$12345678"
|
||||||
return when (this) {
|
return when (this) {
|
||||||
in 0u until 16u -> this.toString()
|
in 0u until 16u -> this.toString()
|
||||||
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
|
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
|
||||||
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
|
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
|
||||||
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
else -> "$"+this.toString(16).padStart(8,'0')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,63 +1,241 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
enum class DataType {
|
import java.util.*
|
||||||
|
|
||||||
|
enum class BaseDataType {
|
||||||
UBYTE, // pass by value 8 bits unsigned
|
UBYTE, // pass by value 8 bits unsigned
|
||||||
BYTE, // pass by value 8 bits signed
|
BYTE, // pass by value 8 bits signed
|
||||||
UWORD, // pass by value 16 bits unsigned
|
UWORD, // pass by value 16 bits unsigned
|
||||||
WORD, // pass by value 16 bits signed
|
WORD, // pass by value 16 bits signed
|
||||||
LONG, // pass by value 32 bits signed
|
LONG, // pass by value 32 bits signed
|
||||||
FLOAT, // pass by value machine dependent
|
FLOAT, // pass by value machine dependent
|
||||||
BOOL, // pass by value bit 0 of a 8 bit byte
|
BOOL, // pass by value bit 0 of an 8-bit byte
|
||||||
STR, // pass by reference
|
STR, // pass by reference
|
||||||
ARRAY_UB, // pass by reference
|
ARRAY, // pass by reference, subtype is the element type
|
||||||
ARRAY_B, // pass by reference
|
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||||
ARRAY_UW, // pass by reference
|
|
||||||
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
|
|
||||||
ARRAY_W, // pass by reference
|
|
||||||
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
|
|
||||||
ARRAY_F, // pass by reference
|
|
||||||
ARRAY_BOOL, // pass by reference
|
|
||||||
UNDEFINED;
|
UNDEFINED;
|
||||||
|
|
||||||
/**
|
|
||||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
|
||||||
*/
|
|
||||||
infix fun isAssignableTo(targetType: DataType) =
|
|
||||||
when(this) {
|
|
||||||
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
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun oneOf(vararg types: DataType) = this in types
|
fun largerSizeThan(other: BaseDataType) =
|
||||||
|
|
||||||
infix fun largerThan(other: DataType) =
|
|
||||||
when {
|
when {
|
||||||
this == other -> false
|
this == other -> false
|
||||||
this in ByteDatatypesWithBoolean -> false
|
this.isByteOrBool -> false
|
||||||
this in WordDatatypes -> other in ByteDatatypesWithBoolean
|
this.isWord -> other.isByteOrBool
|
||||||
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
|
this == LONG -> other.isByteOrBool || other.isWord
|
||||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||||
|
this.isArray && other.isArray -> false
|
||||||
|
this.isArray -> other != FLOAT
|
||||||
|
this == STR -> other != FLOAT
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun equalsSize(other: DataType) =
|
fun equalsSize(other: BaseDataType) =
|
||||||
when {
|
when {
|
||||||
this == other -> true
|
this == other -> true
|
||||||
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
|
this.isArray && other.isArray -> true
|
||||||
this in WordDatatypes -> other in WordDatatypes
|
this.isByteOrBool -> other.isByteOrBool
|
||||||
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
this.isWord -> other.isWord
|
||||||
|
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||||
|
this == STR && other.isArray -> true
|
||||||
|
this.isArray && other == STR -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||||
|
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
|
||||||
|
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
|
||||||
|
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||||
|
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
|
||||||
|
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||||
|
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||||
|
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||||
|
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||||
|
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||||
|
val BaseDataType.isPassByRef get() = this.isIterable
|
||||||
|
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||||
|
|
||||||
|
|
||||||
|
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(base.isArray) {
|
||||||
|
require(sub != null)
|
||||||
|
if(base.isSplitWordArray)
|
||||||
|
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||||
|
}
|
||||||
|
else if(base==BaseDataType.STR)
|
||||||
|
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||||
|
else
|
||||||
|
require(sub == null) { "only string and array base types can have a subtype"}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is DataType) return false
|
||||||
|
return base == other.base && sub == other.sub
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||||
|
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||||
|
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||||
|
val WORD = DataType(BaseDataType.WORD, null)
|
||||||
|
val LONG = DataType(BaseDataType.LONG, null)
|
||||||
|
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||||
|
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||||
|
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||||
|
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||||
|
|
||||||
|
private val simpletypes = mapOf(
|
||||||
|
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||||
|
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||||
|
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||||
|
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||||
|
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||||
|
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||||
|
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||||
|
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||||
|
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||||
|
|
||||||
|
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||||
|
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||||
|
return if(splitwordarray && actualElementDt.isWord)
|
||||||
|
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||||
|
else {
|
||||||
|
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||||
|
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||||
|
else
|
||||||
|
throw NoSuchElementException("invalid element dt $elementDt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||||
|
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||||
|
else arrayFor(base, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun elementType(): DataType =
|
||||||
|
if(base.isArray || base==BaseDataType.STR)
|
||||||
|
forDt(sub!!)
|
||||||
|
else
|
||||||
|
throw IllegalArgumentException("not an array")
|
||||||
|
|
||||||
|
override fun toString(): String = when(base) {
|
||||||
|
BaseDataType.ARRAY -> {
|
||||||
|
when(sub) {
|
||||||
|
BaseDataType.BOOL -> "bool[]"
|
||||||
|
BaseDataType.FLOAT -> "float[]"
|
||||||
|
BaseDataType.BYTE -> "byte[]"
|
||||||
|
BaseDataType.WORD -> "word[]"
|
||||||
|
BaseDataType.UBYTE -> "ubyte[]"
|
||||||
|
BaseDataType.UWORD -> "uword[]"
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BaseDataType.ARRAY_SPLITW -> {
|
||||||
|
when(sub) {
|
||||||
|
BaseDataType.WORD -> "word[] (split)"
|
||||||
|
BaseDataType.UWORD -> "uword[] (split)"
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> base.name.lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sourceString(): String = when (base) {
|
||||||
|
BaseDataType.BOOL -> "bool"
|
||||||
|
BaseDataType.UBYTE -> "ubyte"
|
||||||
|
BaseDataType.BYTE -> "byte"
|
||||||
|
BaseDataType.UWORD -> "uword"
|
||||||
|
BaseDataType.WORD -> "word"
|
||||||
|
BaseDataType.LONG -> "long"
|
||||||
|
BaseDataType.FLOAT -> "float"
|
||||||
|
BaseDataType.STR -> "str"
|
||||||
|
BaseDataType.ARRAY -> {
|
||||||
|
when(sub) {
|
||||||
|
BaseDataType.UBYTE -> "ubyte["
|
||||||
|
BaseDataType.UWORD -> "@nosplit uword["
|
||||||
|
BaseDataType.BOOL -> "bool["
|
||||||
|
BaseDataType.BYTE -> "byte["
|
||||||
|
BaseDataType.WORD -> "@nosplit word["
|
||||||
|
BaseDataType.FLOAT -> "float["
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BaseDataType.ARRAY_SPLITW -> {
|
||||||
|
when(sub) {
|
||||||
|
BaseDataType.UWORD -> "uword["
|
||||||
|
BaseDataType.WORD -> "word["
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||||
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
|
when(base) {
|
||||||
|
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||||
|
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||||
|
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||||
|
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||||
|
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||||
|
BaseDataType.UNDEFINED -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||||
|
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||||
|
|
||||||
|
val isUndefined = base == BaseDataType.UNDEFINED
|
||||||
|
val isByte = base.isByte
|
||||||
|
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||||
|
val isSignedByte = base == BaseDataType.BYTE
|
||||||
|
val isByteOrBool = base.isByteOrBool
|
||||||
|
val isWord = base.isWord
|
||||||
|
val isUnsignedWord = base == BaseDataType.UWORD
|
||||||
|
val isSignedWord = base == BaseDataType.WORD
|
||||||
|
val isInteger = base.isInteger
|
||||||
|
val isIntegerOrBool = base.isIntegerOrBool
|
||||||
|
val isNumeric = base.isNumeric
|
||||||
|
val isNumericOrBool = base.isNumericOrBool
|
||||||
|
val isSigned = base.isSigned
|
||||||
|
val isUnsigned = !base.isSigned
|
||||||
|
val isArray = base.isArray
|
||||||
|
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||||
|
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||||
|
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||||
|
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||||
|
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||||
|
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||||
|
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||||
|
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||||
|
val isString = base == BaseDataType.STR
|
||||||
|
val isBool = base == BaseDataType.BOOL
|
||||||
|
val isFloat = base == BaseDataType.FLOAT
|
||||||
|
val isLong = base == BaseDataType.LONG
|
||||||
|
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||||
|
val isSplitWordArray = base.isSplitWordArray
|
||||||
|
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||||
|
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||||
|
val isIterable = base.isIterable
|
||||||
|
val isPassByRef = base.isPassByRef
|
||||||
|
val isPassByValue = base.isPassByValue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
@ -95,6 +273,17 @@ enum class RegisterOrPair {
|
|||||||
else -> throw IllegalArgumentException("no cpu hardware register for $this")
|
else -> throw IllegalArgumentException("no cpu hardware register for $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun asScopedNameVirtualReg(type: DataType?): List<String> {
|
||||||
|
require(this in Cx16VirtualRegisters)
|
||||||
|
val suffix = when(type?.base) {
|
||||||
|
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
|
||||||
|
BaseDataType.BYTE -> "sL"
|
||||||
|
BaseDataType.WORD -> "s"
|
||||||
|
BaseDataType.UWORD, null -> ""
|
||||||
|
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||||
|
}
|
||||||
|
return listOf("cx16", name.lowercase()+suffix)
|
||||||
|
}
|
||||||
} // only used in parameter and return value specs in asm subroutines
|
} // only used in parameter and return value specs in asm subroutines
|
||||||
|
|
||||||
enum class Statusflag {
|
enum class Statusflag {
|
||||||
@ -123,48 +312,6 @@ enum class BranchCondition {
|
|||||||
VC
|
VC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.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)
|
|
||||||
val IterableDatatypes = arrayOf(
|
|
||||||
DataType.STR,
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
|
||||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
|
||||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
|
||||||
)
|
|
||||||
val PassByValueDatatypes = NumericDatatypesWithBoolean
|
|
||||||
val PassByReferenceDatatypes = IterableDatatypes
|
|
||||||
val ArrayToElementTypes = mapOf(
|
|
||||||
DataType.STR to DataType.UBYTE,
|
|
||||||
DataType.ARRAY_B to DataType.BYTE,
|
|
||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
|
||||||
DataType.ARRAY_W to DataType.WORD,
|
|
||||||
DataType.ARRAY_UW to DataType.UWORD,
|
|
||||||
DataType.ARRAY_W_SPLIT to DataType.WORD,
|
|
||||||
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
|
|
||||||
DataType.ARRAY_F to DataType.FLOAT,
|
|
||||||
DataType.ARRAY_BOOL to DataType.BOOL
|
|
||||||
)
|
|
||||||
val ElementToArrayTypes = mapOf(
|
|
||||||
DataType.BYTE to DataType.ARRAY_B,
|
|
||||||
DataType.UBYTE to DataType.ARRAY_UB,
|
|
||||||
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.STR to DataType.ARRAY_UW // array of str is just an array of pointers
|
|
||||||
)
|
|
||||||
|
|
||||||
val Cx16VirtualRegisters = arrayOf(
|
val Cx16VirtualRegisters = arrayOf(
|
||||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||||
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
||||||
@ -172,7 +319,7 @@ val Cx16VirtualRegisters = arrayOf(
|
|||||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||||
)
|
)
|
||||||
|
|
||||||
val CpuRegisters = setOf(
|
val CpuRegisters = arrayOf(
|
||||||
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||||
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||||
)
|
)
|
||||||
@ -181,7 +328,8 @@ val CpuRegisters = setOf(
|
|||||||
enum class OutputType {
|
enum class OutputType {
|
||||||
RAW,
|
RAW,
|
||||||
PRG,
|
PRG,
|
||||||
XEX
|
XEX,
|
||||||
|
LIBRARY
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CbmPrgLauncherType {
|
enum class CbmPrgLauncherType {
|
||||||
@ -203,3 +351,9 @@ enum class ZeropageWish {
|
|||||||
DONTCARE,
|
DONTCARE,
|
||||||
NOT_IN_ZEROPAGE
|
NOT_IN_ZEROPAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SplitWish {
|
||||||
|
DONTCARE,
|
||||||
|
SPLIT,
|
||||||
|
NOSPLIT
|
||||||
|
}
|
@ -1,8 +1,43 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
enum class CpuType {
|
||||||
|
CPU6502,
|
||||||
|
CPU65C02,
|
||||||
|
VIRTUAL
|
||||||
|
}
|
||||||
|
|
||||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||||
val name: String
|
val name: String
|
||||||
val machine: IMachineDefinition
|
|
||||||
|
val FLOAT_MAX_NEGATIVE: Double
|
||||||
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
|
val FLOAT_MEM_SIZE: Int
|
||||||
|
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||||
|
val PROGRAM_LOAD_ADDRESS : UInt
|
||||||
|
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||||
|
val BSSHIGHRAM_START: UInt
|
||||||
|
val BSSHIGHRAM_END: UInt
|
||||||
|
val BSSGOLDENRAM_START: UInt
|
||||||
|
val BSSGOLDENRAM_END: UInt
|
||||||
|
|
||||||
|
val cpu: CpuType
|
||||||
|
var zeropage: Zeropage
|
||||||
|
var golden: GoldenRam
|
||||||
|
val libraryPath: Path?
|
||||||
|
val customLauncher: List<String>
|
||||||
|
val additionalAssemblerOptions: String?
|
||||||
|
val defaultOutputType: OutputType
|
||||||
|
|
||||||
|
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||||
|
fun getFloatAsmBytes(num: Number): String
|
||||||
|
|
||||||
|
fun convertFloatToBytes(num: Double): List<UByte>
|
||||||
|
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||||
|
|
||||||
|
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||||
|
fun isIOAddress(address: UInt): Boolean
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
|
@ -13,4 +13,6 @@ interface IErrorReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun noErrorForLine(position: Position): Boolean
|
fun noErrorForLine(position: Position): Boolean
|
||||||
|
|
||||||
|
fun printSingleError(errormessage: String)
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package prog8.code.core
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
enum class CpuType {
|
|
||||||
CPU6502,
|
|
||||||
CPU65c02,
|
|
||||||
VIRTUAL
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IMachineDefinition {
|
|
||||||
val FLOAT_MAX_NEGATIVE: Double
|
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
|
||||||
val FLOAT_MEM_SIZE: Int
|
|
||||||
val 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
|
|
||||||
var golden: GoldenRam
|
|
||||||
|
|
||||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
|
||||||
fun getFloatAsmBytes(num: Number): String
|
|
||||||
|
|
||||||
fun convertFloatToBytes(num: Double): List<UByte>
|
|
||||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
|
||||||
|
|
||||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
|
||||||
fun isIOAddress(address: UInt): Boolean
|
|
||||||
}
|
|
@ -1,6 +1,15 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
interface IMemSizer {
|
interface IMemSizer {
|
||||||
fun memorySize(dt: DataType): Int
|
fun memorySize(dt: DataType, numElements: Int?): Int
|
||||||
fun memorySize(arrayDt: DataType, numElements: Int): Int
|
|
||||||
|
fun memorySize(dt: BaseDataType): Int {
|
||||||
|
if(dt.isPassByRef)
|
||||||
|
return memorySize(DataType.UWORD, null) // a pointer size
|
||||||
|
try {
|
||||||
|
return memorySize(DataType.forDt(dt), null)
|
||||||
|
} catch (x: NoSuchElementException) {
|
||||||
|
throw IllegalArgumentException(x.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ enum class Encoding(val prefix: String) {
|
|||||||
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||||
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||||
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
||||||
KATAKANA("kata") // cx16 (katakana)
|
KATAKANA("kata"), // cx16 (katakana)
|
||||||
|
C64OS("c64os") // c64 (C64 OS)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IStringEncoding {
|
interface IStringEncoding {
|
||||||
|
@ -22,7 +22,7 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
|||||||
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||||
|
|
||||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||||
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
|
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
for (reserved in options.zpReserved)
|
for (reserved in options.zpReserved)
|
||||||
reserve(reserved)
|
reserve(reserved)
|
||||||
|
|
||||||
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,9 +70,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
return Err(MemAllocationError("zero page usage has been disabled"))
|
return Err(MemAllocationError("zero page usage has been disabled"))
|
||||||
|
|
||||||
val size: Int =
|
val size: Int =
|
||||||
when (datatype) {
|
when {
|
||||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||||
DataType.STR, in ArrayDatatypes -> {
|
datatype.isString || datatype.isArray -> {
|
||||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||||
if(position!=null)
|
if(position!=null)
|
||||||
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
|
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
|
||||||
@ -80,9 +80,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
|
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
|
||||||
memsize
|
memsize
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
datatype.isFloat -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
val memsize = options.compTarget.memorySize(DataType.FLOAT)
|
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||||
if(position!=null)
|
if(position!=null)
|
||||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||||
else
|
else
|
||||||
@ -94,7 +94,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(free.size > 0) {
|
if(free.isNotEmpty()) {
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
if(oneSeparateByteFree(candidate))
|
if(oneSeparateByteFree(candidate))
|
||||||
@ -118,10 +118,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
require(size>=0)
|
require(size>=0)
|
||||||
free.removeAll(address until address+size.toUInt())
|
free.removeAll(address until address+size.toUInt())
|
||||||
if(name.isNotEmpty()) {
|
if(name.isNotEmpty()) {
|
||||||
allocatedVariables[name] = when(datatype) {
|
allocatedVariables[name] = when {
|
||||||
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.isNumericOrBool -> 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)
|
datatype.isString -> VarAllocation(address, datatype, size)
|
||||||
in ArrayDatatypes -> VarAllocation(address, datatype, size)
|
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||||
else -> throw AssemblyError("invalid dt")
|
else -> throw AssemblyError("invalid dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,8 +133,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
require(size>0)
|
require(size>0)
|
||||||
return free.containsAll((address until address+size.toUInt()).toList())
|
return free.containsAll((address until address+size.toUInt()).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun allocateCx16VirtualRegisters()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -150,14 +148,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||||||
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||||
|
|
||||||
val size: Int =
|
val size: Int =
|
||||||
when (datatype) {
|
when {
|
||||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||||
DataType.STR, in ArrayDatatypes -> {
|
datatype.isString -> numElements!!
|
||||||
options.compTarget.memorySize(datatype, numElements!!)
|
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||||
}
|
datatype.isFloat -> {
|
||||||
DataType.FLOAT -> {
|
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
options.compTarget.memorySize(DataType.FLOAT)
|
options.compTarget.memorySize(DataType.FLOAT, null)
|
||||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
}
|
}
|
||||||
else -> throw MemAllocationError("weird dt")
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation
|
val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
|
||||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
|
val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
|
||||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
val BitwiseOperators = arrayOf("&", "|", "^", "~")
|
||||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
val PrefixOperators = arrayOf("+", "-", "~", "not")
|
||||||
|
|
||||||
fun invertedComparisonOperator(operator: String) =
|
fun invertedComparisonOperator(operator: String) =
|
||||||
when (operator) {
|
when (operator) {
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
import prog8.code.sanitize
|
||||||
|
import prog8.code.source.SourceCode
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
fun toClickableStr(): String {
|
fun toClickableStr(): String {
|
||||||
if(this===DUMMY)
|
if(this===DUMMY)
|
||||||
return ""
|
return ""
|
||||||
if(file.startsWith(LIBRARYFILEPREFIX))
|
if(SourceCode.isLibraryResource(file))
|
||||||
return "$file:$line:$startCol:"
|
return "$file:$line:$startCol:"
|
||||||
return try {
|
return try {
|
||||||
val path = Path(file).absolute().normalize().toString()
|
val path = Path(file).sanitize().toString()
|
||||||
"file://$path:$line:$startCol:"
|
"file://$path:$line:$startCol:"
|
||||||
} catch(x: InvalidPathException) {
|
} catch(_: InvalidPathException) {
|
||||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||||
"file://$file:$line:$startCol:"
|
"file://$file:$line:$startCol:"
|
||||||
}
|
}
|
||||||
|
74
codeCore/src/prog8/code/source/ImportFileSystem.kt
Normal file
74
codeCore/src/prog8/code/source/ImportFileSystem.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package prog8.code.source
|
||||||
|
|
||||||
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.sanitize
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
|
||||||
|
// Resource caching "filesystem".
|
||||||
|
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
|
||||||
|
|
||||||
|
object ImportFileSystem {
|
||||||
|
|
||||||
|
fun expandTilde(path: String): String = if (path.startsWith("~")) {
|
||||||
|
val userHome = System.getProperty("user.home")
|
||||||
|
userHome + path.drop(1)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||||
|
|
||||||
|
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||||
|
val normalized = path.sanitize()
|
||||||
|
val cached = cache[normalized.toString()]
|
||||||
|
if (cached != null)
|
||||||
|
return cached
|
||||||
|
val file = SourceCode.File(normalized, isLibrary)
|
||||||
|
cache[normalized.toString()] = file
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getResource(name: String): SourceCode {
|
||||||
|
val cached = cache[name]
|
||||||
|
if (cached != null) return cached
|
||||||
|
val resource = SourceCode.Resource(name)
|
||||||
|
cache[name] = resource
|
||||||
|
return resource
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retrieveSourceLine(position: Position): String {
|
||||||
|
if(SourceCode.isLibraryResource(position.file)) {
|
||||||
|
val cached = cache[SourceCode.withoutPrefix(position.file)]
|
||||||
|
if(cached != null)
|
||||||
|
return getLine(cached, position.line)
|
||||||
|
}
|
||||||
|
val cached = cache[position.file]
|
||||||
|
if(cached != null)
|
||||||
|
return getLine(cached, position.line)
|
||||||
|
val path = Path(position.file).sanitize()
|
||||||
|
val cached2 = cache[path.toString()]
|
||||||
|
if(cached2 != null)
|
||||||
|
return getLine(cached2, position.line)
|
||||||
|
throw NoSuchElementException("cannot get source line $position, with path $path")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLine(code: SourceCode, lineIndex: Int): String {
|
||||||
|
var spans = lineSpanCache[code]
|
||||||
|
if(spans==null) {
|
||||||
|
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
|
||||||
|
val ends = lineSpans.drop(1) + code.text.length
|
||||||
|
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
|
||||||
|
lineSpanCache[code] = spans
|
||||||
|
}
|
||||||
|
val span = spans[lineIndex - 1]
|
||||||
|
return code.text.substring(span.start, span.end).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LineSpan(val start: Int, val end: Int)
|
||||||
|
|
||||||
|
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
|
||||||
|
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
|
||||||
|
}
|
@ -1,16 +1,13 @@
|
|||||||
package prog8.code.core
|
package prog8.code.source
|
||||||
|
|
||||||
import java.io.File
|
import prog8.code.sanitize
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.text.Normalizer
|
import java.text.Normalizer
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
import kotlin.io.path.readText
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
|
||||||
const val internedStringsModuleName = "prog8_interned_strings"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
||||||
*/
|
*/
|
||||||
@ -26,6 +23,11 @@ sealed class SourceCode {
|
|||||||
*/
|
*/
|
||||||
abstract val isFromFilesystem: Boolean
|
abstract val isFromFilesystem: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this [SourceCode] instance was created from a library module file
|
||||||
|
*/
|
||||||
|
abstract val isFromLibrary: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logical name of the source code unit. Usually the module's name.
|
* The logical name of the source code unit. Usually the module's name.
|
||||||
*/
|
*/
|
||||||
@ -55,14 +57,21 @@ sealed class SourceCode {
|
|||||||
/**
|
/**
|
||||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||||
*/
|
*/
|
||||||
const val LIBRARYFILEPREFIX = "library:"
|
private const val LIBRARYFILEPREFIX = "library:"
|
||||||
const val STRINGSOURCEPREFIX = "string:"
|
private const val STRINGSOURCEPREFIX = "string:"
|
||||||
val curdir: Path = Path(".").toAbsolutePath()
|
val curdir: Path = Path(".").absolute()
|
||||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||||
fun isRegularFilesystemPath(pathString: String) =
|
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||||
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
|
|
||||||
|
|
||||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||||
|
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||||
|
fun withoutPrefix(path: String): String {
|
||||||
|
return if(isLibraryResource(path))
|
||||||
|
path.removePrefix(LIBRARYFILEPREFIX)
|
||||||
|
else if(isStringResource(path))
|
||||||
|
path.removePrefix(STRINGSOURCEPREFIX)
|
||||||
|
else
|
||||||
|
path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,6 +82,7 @@ sealed class SourceCode {
|
|||||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||||
override val isFromResources = false
|
override val isFromResources = false
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = false
|
||||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||||
override val name = "<unnamed-text>"
|
override val name = "<unnamed-text>"
|
||||||
}
|
}
|
||||||
@ -80,12 +90,13 @@ sealed class SourceCode {
|
|||||||
/**
|
/**
|
||||||
* Get [SourceCode] from the file represented by the specified Path.
|
* Get [SourceCode] from the file represented by the specified Path.
|
||||||
* This immediately reads the file fully into memory.
|
* This immediately reads the file fully into memory.
|
||||||
|
* You can only get an instance of this via the ImportFileSystem object.
|
||||||
*
|
*
|
||||||
* [origin] will be the given path in absolute and normalized form.
|
* [origin] will be the given path in absolute and normalized form.
|
||||||
* @throws NoSuchFileException if the file does not exist
|
* @throws NoSuchFileException if the file does not exist
|
||||||
* @throws FileSystemException if the file cannot be read
|
* @throws FileSystemException if the file cannot be read
|
||||||
*/
|
*/
|
||||||
class File(path: Path): SourceCode() {
|
internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
|
||||||
override val text: String
|
override val text: String
|
||||||
override val origin: String
|
override val origin: String
|
||||||
override val name: String
|
override val name: String
|
||||||
@ -109,12 +120,14 @@ sealed class SourceCode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
||||||
|
* You can only get an instance of this via the ImportFileSystem object.
|
||||||
*/
|
*/
|
||||||
class Resource(pathString: String): SourceCode() {
|
internal class Resource(pathString: String): SourceCode() {
|
||||||
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
|
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
|
||||||
|
|
||||||
override val isFromResources = true
|
override val isFromResources = true
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = true
|
||||||
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||||
override val text: String
|
override val text: String
|
||||||
override val name: String
|
override val name: String
|
||||||
@ -124,7 +137,7 @@ sealed class SourceCode {
|
|||||||
if (rscURL == null) {
|
if (rscURL == null) {
|
||||||
val rscRoot = object {}.javaClass.getResource("/")
|
val rscRoot = object {}.javaClass.getResource("/")
|
||||||
throw NoSuchFileException(
|
throw NoSuchFileException(
|
||||||
File(normalized),
|
java.io.File(normalized),
|
||||||
reason = "looked in resources rooted at $rscRoot"
|
reason = "looked in resources rooted at $rscRoot"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -141,37 +154,8 @@ sealed class SourceCode {
|
|||||||
class Generated(override val name: String) : SourceCode() {
|
class Generated(override val name: String) : SourceCode() {
|
||||||
override val isFromResources: Boolean = false
|
override val isFromResources: Boolean = false
|
||||||
override val isFromFilesystem: Boolean = false
|
override val isFromFilesystem: Boolean = false
|
||||||
|
override val isFromLibrary: Boolean = false
|
||||||
override val origin: String = name
|
override val origin: String = name
|
||||||
override val text: String = "<generated code node, no text representation>"
|
override val text: String = "<generated code node, no text representation>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
object SourceLineCache {
|
|
||||||
private val cache = mutableMapOf<String, List<String>>()
|
|
||||||
|
|
||||||
private fun getCachedFile(file: String): List<String> {
|
|
||||||
val existing = cache[file]
|
|
||||||
if(existing!=null)
|
|
||||||
return existing
|
|
||||||
if (SourceCode.isRegularFilesystemPath(file)) {
|
|
||||||
val source = SourceCode.File(Path(file))
|
|
||||||
cache[file] = source.text.split('\n', '\r').map { it.trim() }
|
|
||||||
return cache.getValue(file)
|
|
||||||
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
|
|
||||||
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
|
|
||||||
cache[file] = source.text.split('\n', '\r').map { it.trim()}
|
|
||||||
return cache.getValue(file)
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun retrieveLine(position: Position): String? {
|
|
||||||
if (position.line>0) {
|
|
||||||
val lines = getCachedFile(position.file)
|
|
||||||
if(lines.isNotEmpty())
|
|
||||||
return lines[position.line-1]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package prog8.code.target
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.atari.AtariMachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|
||||||
override val name = NAME
|
|
||||||
override val machine = AtariMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.ATASCII
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val NAME = "atari"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypesWithBoolean -> 1
|
|
||||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
|
||||||
else -> throw IllegalArgumentException("invalid datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
|
||||||
if(arrayDt==DataType.UWORD)
|
|
||||||
numElements // pointer to bytes.
|
|
||||||
else
|
|
||||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +1,81 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.C128Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.c128.C128MachineDefinition
|
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
|
|
||||||
|
|
||||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class C128Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = C128MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c128"
|
const val NAME = "c128"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // TODO
|
||||||
|
override val BSSHIGHRAM_END = 0u // TODO
|
||||||
|
override val BSSGOLDENRAM_START = 0u // TODO
|
||||||
|
override val BSSGOLDENRAM_END = 0u // TODO
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting C-128 emulator x128...")
|
||||||
|
|
||||||
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C128Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,95 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.C64Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.io.IOException
|
||||||
import prog8.code.target.c64.C64MachineDefinition
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
|
|
||||||
|
|
||||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class C64Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = C64MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c64"
|
const val NAME = "c64"
|
||||||
|
|
||||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||||
|
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xc000u
|
||||||
|
override val BSSHIGHRAM_END = 0xcfdfu
|
||||||
|
override val BSSGOLDENRAM_START = 0u // no golden ram on C64
|
||||||
|
override val BSSGOLDENRAM_END = 0u
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
|
||||||
|
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(_: IOException) {
|
||||||
|
continue // try the next emulator executable
|
||||||
|
}
|
||||||
|
process.waitFor()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C64Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -26,7 +98,6 @@ val CompilationTargets = listOf(
|
|||||||
C128Target.NAME,
|
C128Target.NAME,
|
||||||
Cx16Target.NAME,
|
Cx16Target.NAME,
|
||||||
PETTarget.NAME,
|
PETTarget.NAME,
|
||||||
AtariTarget.NAME,
|
|
||||||
VMTarget.NAME
|
VMTarget.NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,7 +106,6 @@ fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
|
|||||||
C128Target.NAME -> C128Target()
|
C128Target.NAME -> C128Target()
|
||||||
Cx16Target.NAME -> Cx16Target()
|
Cx16Target.NAME -> Cx16Target()
|
||||||
PETTarget.NAME -> PETTarget()
|
PETTarget.NAME -> PETTarget()
|
||||||
AtariTarget.NAME -> AtariTarget()
|
|
||||||
VMTarget.NAME -> VMTarget()
|
VMTarget.NAME -> VMTarget()
|
||||||
else -> throw IllegalArgumentException("invalid compilation target")
|
else -> throw IllegalArgumentException("invalid compilation target")
|
||||||
}
|
}
|
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.source.ImportFileSystem.expandTilde
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.ConfigurableZeropage
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
|
import kotlin.io.path.isDirectory
|
||||||
|
import kotlin.io.path.nameWithoutExtension
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFileTarget(
|
||||||
|
override val name: String,
|
||||||
|
override val defaultEncoding: Encoding,
|
||||||
|
override val cpu: CpuType,
|
||||||
|
override val PROGRAM_LOAD_ADDRESS: UInt,
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS: UInt,
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE: UInt,
|
||||||
|
override val BSSHIGHRAM_START: UInt,
|
||||||
|
override val BSSHIGHRAM_END: UInt,
|
||||||
|
override val BSSGOLDENRAM_START: UInt,
|
||||||
|
override val BSSGOLDENRAM_END: UInt,
|
||||||
|
override val defaultOutputType: OutputType,
|
||||||
|
override val libraryPath: Path,
|
||||||
|
override val customLauncher: List<String>,
|
||||||
|
override val additionalAssemblerOptions: String?,
|
||||||
|
val ioAddresses: List<UIntRange>,
|
||||||
|
val zpScratchB1: UInt,
|
||||||
|
val zpScratchReg: UInt,
|
||||||
|
val zpScratchW1: UInt,
|
||||||
|
val zpScratchW2: UInt,
|
||||||
|
val virtualregistersStart: UInt,
|
||||||
|
val zpFullsafe: List<UIntRange>,
|
||||||
|
val zpKernalsafe: List<UIntRange>,
|
||||||
|
val zpBasicsafe: List<UIntRange>
|
||||||
|
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun Properties.getString(property: String): String {
|
||||||
|
val value = this.getProperty(property, null)
|
||||||
|
if(value!=null)
|
||||||
|
return value
|
||||||
|
throw NoSuchElementException("string property '$property' not found in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Properties.getInteger(property: String): UInt {
|
||||||
|
val value = this.getProperty(property, null)
|
||||||
|
if(value!=null) return parseInt(value)
|
||||||
|
throw NoSuchElementException("integer property '$property' not found in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseInt(value: String): UInt {
|
||||||
|
if(value.startsWith("0x"))
|
||||||
|
return value.drop(2).toUInt(16)
|
||||||
|
if(value.startsWith("$"))
|
||||||
|
return value.drop(1).toUInt(16)
|
||||||
|
if(value.startsWith("%"))
|
||||||
|
return value.drop(1).toUInt(2)
|
||||||
|
return value.toUInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
|
||||||
|
val rangesStr = props.getString(key)
|
||||||
|
if(rangesStr.isBlank())
|
||||||
|
return emptyList()
|
||||||
|
val result = mutableListOf<UIntRange>()
|
||||||
|
val ranges = rangesStr.split(",").map { it.trim() }
|
||||||
|
for(r in ranges) {
|
||||||
|
if ('-' in r) {
|
||||||
|
val (fromStr, toStr) = r.split("-")
|
||||||
|
val from = parseInt(fromStr.trim())
|
||||||
|
val to = parseInt(toStr.trim())
|
||||||
|
result.add(from..to)
|
||||||
|
} else {
|
||||||
|
val address = parseInt(r)
|
||||||
|
result.add(address..address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromConfigFile(configfile: Path): ConfigFileTarget {
|
||||||
|
val props = Properties()
|
||||||
|
props.load(configfile.inputStream())
|
||||||
|
|
||||||
|
val cpuString = props.getString("cpu").uppercase()
|
||||||
|
val cpuType = try {
|
||||||
|
CpuType.valueOf(cpuString)
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
CpuType.valueOf("CPU$cpuString")
|
||||||
|
}
|
||||||
|
val ioAddresses = parseAddressRanges("io_regions", props)
|
||||||
|
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
|
||||||
|
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
|
||||||
|
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
|
||||||
|
|
||||||
|
val libraryPath = expandTilde(Path(props.getString("library")))
|
||||||
|
if(!libraryPath.isDirectory())
|
||||||
|
throw IOException("invalid library path: $libraryPath")
|
||||||
|
|
||||||
|
val customLauncherStr = props.getProperty("custom_launcher_code", null)
|
||||||
|
val customLauncher =
|
||||||
|
if(customLauncherStr?.isNotBlank()==true)
|
||||||
|
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||||
|
else emptyList()
|
||||||
|
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||||
|
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||||
|
|
||||||
|
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||||
|
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||||
|
|
||||||
|
return ConfigFileTarget(
|
||||||
|
configfile.nameWithoutExtension,
|
||||||
|
Encoding.entries.first { it.prefix==props.getString("encoding") },
|
||||||
|
cpuType,
|
||||||
|
props.getInteger("load_address"),
|
||||||
|
props.getInteger("memtop"),
|
||||||
|
0u, // used only in a very specific error condition check in a certain scenario...
|
||||||
|
props.getInteger("bss_highram_start"),
|
||||||
|
props.getInteger("bss_highram_end"),
|
||||||
|
props.getInteger("bss_goldenram_start"),
|
||||||
|
props.getInteger("bss_goldenram_end"),
|
||||||
|
defaultOutputType,
|
||||||
|
libraryPath,
|
||||||
|
customLauncher,
|
||||||
|
assemblerOptions,
|
||||||
|
ioAddresses,
|
||||||
|
props.getInteger("zp_scratch_b1"),
|
||||||
|
props.getInteger("zp_scratch_reg"),
|
||||||
|
props.getInteger("zp_scratch_w1"),
|
||||||
|
props.getInteger("zp_scratch_w2"),
|
||||||
|
props.getInteger("virtual_registers"),
|
||||||
|
zpFullsafe,
|
||||||
|
zpKernalsafe,
|
||||||
|
zpBasicsafe,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO floats are not yet supported here, just enter some values
|
||||||
|
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||||
|
override val FLOAT_MEM_SIZE = 8
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam // TODO this is not yet used
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = ConfigurableZeropage(
|
||||||
|
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||||
|
virtualregistersStart,
|
||||||
|
zpBasicsafe,
|
||||||
|
zpKernalsafe,
|
||||||
|
zpFullsafe,
|
||||||
|
compilerOptions
|
||||||
|
)
|
||||||
|
// note: there's no golden ram yet
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,94 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.CX16Zeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
import prog8.code.target.cx16.CX16MachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class Cx16Target: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = CX16MachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "cx16"
|
const val NAME = "cx16"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU65C02
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||||
|
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||||
|
override val BSSGOLDENRAM_START = 0x0400u
|
||||||
|
override val BSSGOLDENRAM_END = 0x07ffu
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
val emulator: String
|
||||||
|
val extraArgs: List<String>
|
||||||
|
|
||||||
|
when(selectedEmulator) {
|
||||||
|
1 -> {
|
||||||
|
emulator = "x16emu"
|
||||||
|
extraArgs = listOf("-debug")
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
emulator = "box16"
|
||||||
|
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
|
||||||
|
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package prog8.code.target.cbm
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
34
codeCore/src/prog8/code/target/NormalMemSizer.kt
Normal file
34
codeCore/src/prog8/code/target/NormalMemSizer.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.BaseDataType
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
|
||||||
|
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||||
|
if(dt.isArray) {
|
||||||
|
if(numElements==null) return 2 // treat it as a pointer size
|
||||||
|
return when(dt.sub) {
|
||||||
|
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||||
|
BaseDataType.FLOAT-> numElements * floatsize
|
||||||
|
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dt.isString) {
|
||||||
|
return numElements // treat it as the size of the given string with the length
|
||||||
|
?: 2 // treat it as the size to store a string pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
|
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||||
|
dt.isLong -> 4 * (numElements ?: 1)
|
||||||
|
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
|
else -> 2 * (numElements ?: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,19 +1,80 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.target.encodings.Encoder
|
||||||
import prog8.code.core.IMemSizer
|
import prog8.code.target.zp.PETZeropage
|
||||||
import prog8.code.core.IStringEncoding
|
import java.nio.file.Path
|
||||||
import prog8.code.target.cbm.CbmMemorySizer
|
|
||||||
import prog8.code.target.pet.PETMachineDefinition
|
|
||||||
|
|
||||||
|
|
||||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
class PETTarget: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(true),
|
||||||
|
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = PETMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "pet32"
|
const val NAME = "pet32"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u
|
||||||
|
override val BSSHIGHRAM_END = 0u
|
||||||
|
override val BSSGOLDENRAM_START = 0u
|
||||||
|
override val BSSGOLDENRAM_END = 0u
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val m5 = Mflpt5.fromNumber(num)
|
||||||
|
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==5) { "need 5 bytes" }
|
||||||
|
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||||
|
return m5.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting PET emulator...")
|
||||||
|
|
||||||
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
|
val process=processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = PETZeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,111 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.isReadable
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
class VMTarget: ICompilationTarget,
|
||||||
|
IStringEncoding by Encoder(false),
|
||||||
|
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||||
|
|
||||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|
||||||
override val name = NAME
|
override val name = NAME
|
||||||
override val machine = VirtualMachineDefinition()
|
|
||||||
override val defaultEncoding = Encoding.ISO
|
override val defaultEncoding = Encoding.ISO
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "virtual"
|
const val NAME = "virtual"
|
||||||
|
const val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
override val cpu = CpuType.VIRTUAL
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypesWithBoolean -> 1
|
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||||
else -> throw IllegalArgumentException("invalid datatype")
|
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_END = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||||
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
|
override lateinit var golden: GoldenRam // not actually used
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number): String {
|
||||||
|
// little endian binary representation
|
||||||
|
val bits = num.toDouble().toBits().toULong()
|
||||||
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { "$$it" }
|
||||||
|
return parts.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val bits = num.toBits().toULong()
|
||||||
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==8) { "need 8 bytes" }
|
||||||
|
val b0 = bytes[0].toLong() shl (8*7)
|
||||||
|
val b1 = bytes[1].toLong() shl (8*6)
|
||||||
|
val b2 = bytes[2].toLong() shl (8*5)
|
||||||
|
val b3 = bytes[3].toLong() shl (8*4)
|
||||||
|
val b4 = bytes[4].toLong() shl (8*3)
|
||||||
|
val b5 = bytes[5].toLong() shl (8*2)
|
||||||
|
val b6 = bytes[6].toLong() shl (8*1)
|
||||||
|
val b7 = bytes[7].toLong() shl (8*0)
|
||||||
|
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
|
if(!quiet)
|
||||||
|
println("\nStarting Virtual Machine...")
|
||||||
|
|
||||||
|
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||||
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
|
val filename = programNameWithPath.name
|
||||||
|
if(programNameWithPath.isReadable()) {
|
||||||
|
vm.runProgram(programNameWithPath.readText(), quiet)
|
||||||
|
} else {
|
||||||
|
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||||
|
if(withExt.isReadable())
|
||||||
|
vm.runProgram(withExt.readText(), quiet)
|
||||||
|
else
|
||||||
|
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
if(arrayDt==DataType.UWORD)
|
|
||||||
numElements // pointer to bytes.
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
else
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(irSource: String, quiet: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||||
|
override val SCRATCH_B1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_REG: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W2: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
}
|
}
|
@ -1,60 +0,0 @@
|
|||||||
package prog8.code.target.atari
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class AtariMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
|
||||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
|
||||||
override val FLOAT_MEM_SIZE = 6
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0u // TODO
|
|
||||||
override val BSSHIGHRAM_END = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_START = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_END = 0u // TODO
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
val emulatorName: String
|
|
||||||
val cmdline: List<String>
|
|
||||||
when(selectedEmulator) {
|
|
||||||
1 -> {
|
|
||||||
emulatorName = "atari800"
|
|
||||||
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
emulatorName = "altirra"
|
|
||||||
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
System.err.println("Atari target only supports atari800 and altirra emulators.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO monlist?
|
|
||||||
|
|
||||||
println("\nStarting Atari800XL emulator $emulatorName...")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = AtariZeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package prog8.code.target.atari
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.InternalCompilerException
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.code.core.ZeropageType
|
|
||||||
|
|
||||||
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
|
||||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
|
||||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
|
||||||
ZeropageType.FLOATSAFE,
|
|
||||||
ZeropageType.BASICSAFE,
|
|
||||||
ZeropageType.DONTUSE
|
|
||||||
))
|
|
||||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
|
||||||
|
|
||||||
when (options.zeropage) {
|
|
||||||
ZeropageType.FULL -> {
|
|
||||||
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
|
||||||
free.addAll(0x00u..0xffu)
|
|
||||||
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
|
||||||
}
|
|
||||||
ZeropageType.KERNALSAFE -> {
|
|
||||||
free.addAll(0x80u..0xffu) // TODO
|
|
||||||
}
|
|
||||||
ZeropageType.BASICSAFE,
|
|
||||||
ZeropageType.FLOATSAFE -> {
|
|
||||||
free.addAll(0x80u..0xffu) // TODO
|
|
||||||
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
|
||||||
}
|
|
||||||
ZeropageType.DONTUSE -> {
|
|
||||||
free.clear() // don't use zeropage at all
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
|
||||||
free.clear()
|
|
||||||
free.addAll(distinctFree)
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
retainAllowed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if atari can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package prog8.code.target.c128
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class C128MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0u // TODO
|
|
||||||
override val BSSHIGHRAM_END = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_START = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_END = 0u // TODO
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\nStarting C-128 emulator x128...")
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
|
||||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = C128Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package prog8.code.target.c64
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class C64MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0xc000u
|
|
||||||
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 launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process
|
|
||||||
try {
|
|
||||||
process=processb.start()
|
|
||||||
} catch(x: IOException) {
|
|
||||||
continue // try the next emulator executable
|
|
||||||
}
|
|
||||||
process.waitFor()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = C64Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package prog8.code.target.cbm
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
|
|
||||||
|
|
||||||
internal object CbmMemorySizer: IMemSizer {
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypesWithBoolean -> 1
|
|
||||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
|
||||||
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
else -> throw IllegalArgumentException("invalid datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
|
||||||
if(arrayDt==DataType.UWORD)
|
|
||||||
numElements // pointer to bytes.
|
|
||||||
else
|
|
||||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package prog8.code.target.cx16
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class CX16MachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU65c02
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
|
||||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
|
||||||
override val BSSGOLDENRAM_START = 0x0400u
|
|
||||||
override val BSSGOLDENRAM_END = 0x07ffu
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
val emulator: String
|
|
||||||
val extraArgs: List<String>
|
|
||||||
|
|
||||||
when(selectedEmulator) {
|
|
||||||
1 -> {
|
|
||||||
emulator = "x16emu"
|
|
||||||
extraArgs = listOf("-debug")
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
emulator = "box16"
|
|
||||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\nStarting Commander X16 emulator $emulator...")
|
|
||||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
|
||||||
val process: Process = processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = CX16Zeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -26,7 +26,7 @@ object AtasciiEncoding {
|
|||||||
'▖',
|
'▖',
|
||||||
|
|
||||||
// $10
|
// $10
|
||||||
'♣',
|
'♣',
|
||||||
'┌',
|
'┌',
|
||||||
'─',
|
'─',
|
||||||
'┼',
|
'┼',
|
||||||
@ -62,7 +62,7 @@ object AtasciiEncoding {
|
|||||||
'/',
|
'/',
|
||||||
|
|
||||||
// $30
|
// $30
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
'2',
|
'2',
|
||||||
'3',
|
'3',
|
||||||
@ -80,7 +80,7 @@ object AtasciiEncoding {
|
|||||||
'?',
|
'?',
|
||||||
|
|
||||||
// $40
|
// $40
|
||||||
'@',
|
'@',
|
||||||
'A',
|
'A',
|
||||||
'B',
|
'B',
|
||||||
'C',
|
'C',
|
||||||
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
|||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\r' -> 0x9bu
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
|
||||||
|
object C64osEncoding {
|
||||||
|
|
||||||
|
// decoding: from C64 OS Screencodes (0-255) to unicode
|
||||||
|
// character table from:
|
||||||
|
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
|
||||||
|
|
||||||
|
private val decodingC64os = charArrayOf(
|
||||||
|
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||||
|
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
||||||
|
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
||||||
|
'c' , // c 0x03 -> LATIN SMALL LETTER C
|
||||||
|
'd' , // d 0x04 -> LATIN SMALL LETTER D
|
||||||
|
'e' , // e 0x05 -> LATIN SMALL LETTER E
|
||||||
|
'f' , // f 0x06 -> LATIN SMALL LETTER F
|
||||||
|
'g' , // g 0x07 -> LATIN SMALL LETTER G
|
||||||
|
'h' , // h 0x08 -> LATIN SMALL LETTER H
|
||||||
|
'i' , // i 0x09 -> LATIN SMALL LETTER I
|
||||||
|
'j' , // j 0x0A -> LATIN SMALL LETTER J
|
||||||
|
'k' , // k 0x0B -> LATIN SMALL LETTER K
|
||||||
|
'l' , // l 0x0C -> LATIN SMALL LETTER L
|
||||||
|
'm' , // m 0x0D -> LATIN SMALL LETTER M
|
||||||
|
'n' , // n 0x0E -> LATIN SMALL LETTER N
|
||||||
|
'o' , // o 0x0F -> LATIN SMALL LETTER O
|
||||||
|
'p' , // p 0x10 -> LATIN SMALL LETTER P
|
||||||
|
'q' , // q 0x11 -> LATIN SMALL LETTER Q
|
||||||
|
'r' , // r 0x12 -> LATIN SMALL LETTER R
|
||||||
|
's' , // s 0x13 -> LATIN SMALL LETTER S
|
||||||
|
't' , // t 0x14 -> LATIN SMALL LETTER T
|
||||||
|
'u' , // u 0x15 -> LATIN SMALL LETTER U
|
||||||
|
'v' , // v 0x16 -> LATIN SMALL LETTER V
|
||||||
|
'w' , // w 0x17 -> LATIN SMALL LETTER W
|
||||||
|
'x' , // x 0x18 -> LATIN SMALL LETTER X
|
||||||
|
'y' , // y 0x19 -> LATIN SMALL LETTER Y
|
||||||
|
'z' , // z 0x1A -> LATIN SMALL LETTER Z
|
||||||
|
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
|
||||||
|
'\\' , // \ 0x1C -> REVERSE SOLIDUS
|
||||||
|
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
|
||||||
|
'^' , // ^ 0x1E -> CIRCUMFLEX
|
||||||
|
'_' , // _ 0x1F -> UNDERSCORE
|
||||||
|
' ' , // 0x20 -> SPACE
|
||||||
|
'!' , // ! 0x21 -> EXCLAMATION MARK
|
||||||
|
'"' , // " 0x22 -> QUOTATION MARK
|
||||||
|
'#' , // # 0x23 -> NUMBER SIGN
|
||||||
|
'$' , // $ 0x24 -> DOLLAR SIGN
|
||||||
|
'%' , // % 0x25 -> PERCENT SIGN
|
||||||
|
'&' , // & 0x26 -> AMPERSAND
|
||||||
|
'\'' , // ' 0x27 -> APOSTROPHE
|
||||||
|
'(' , // ( 0x28 -> LEFT PARENTHESIS
|
||||||
|
')' , // ) 0x29 -> RIGHT PARENTHESIS
|
||||||
|
'*' , // * 0x2A -> ASTERISK
|
||||||
|
'+' , // + 0x2B -> PLUS SIGN
|
||||||
|
',' , // , 0x2C -> COMMA
|
||||||
|
'-' , // - 0x2D -> HYPHEN-MINUS
|
||||||
|
'.' , // . 0x2E -> FULL STOP
|
||||||
|
'/' , // / 0x2F -> SOLIDUS
|
||||||
|
'0' , // 0 0x30 -> DIGIT ZERO
|
||||||
|
'1' , // 1 0x31 -> DIGIT ONE
|
||||||
|
'2' , // 2 0x32 -> DIGIT TWO
|
||||||
|
'3' , // 3 0x33 -> DIGIT THREE
|
||||||
|
'4' , // 4 0x34 -> DIGIT FOUR
|
||||||
|
'5' , // 5 0x35 -> DIGIT FIVE
|
||||||
|
'6' , // 6 0x36 -> DIGIT SIX
|
||||||
|
'7' , // 7 0x37 -> DIGIT SEVEN
|
||||||
|
'8' , // 8 0x38 -> DIGIT EIGHT
|
||||||
|
'9' , // 9 0x39 -> DIGIT NINE
|
||||||
|
':' , // : 0x3A -> COLON
|
||||||
|
';' , // ; 0x3B -> SEMICOLON
|
||||||
|
'<' , // < 0x3C -> LESS-THAN SIGN
|
||||||
|
'=' , // = 0x3D -> EQUALS SIGN
|
||||||
|
'>' , // > 0x3E -> GREATER-THAN SIGN
|
||||||
|
'?' , // ? 0x3F -> QUESTION MARK
|
||||||
|
'`' , // ` 0x40 -> GRAVE ACCENT
|
||||||
|
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
|
||||||
|
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
|
||||||
|
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
|
||||||
|
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
|
||||||
|
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
|
||||||
|
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
|
||||||
|
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
|
||||||
|
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
|
||||||
|
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
|
||||||
|
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
|
||||||
|
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
|
||||||
|
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
|
||||||
|
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
|
||||||
|
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
|
||||||
|
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
|
||||||
|
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
|
||||||
|
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
|
||||||
|
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
|
||||||
|
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
|
||||||
|
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
|
||||||
|
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
|
||||||
|
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
|
||||||
|
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
|
||||||
|
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
|
||||||
|
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
|
||||||
|
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
|
||||||
|
'{' , // { 0x5B -> LEFT BRACE
|
||||||
|
'|' , // | 0x5C -> VERTICAL BAR
|
||||||
|
'}' , // } 0x5D -> RIGHT BRACE
|
||||||
|
'~' , // ~ 0x5E -> TILDE
|
||||||
|
'\ufffe', // 0x5F -> RESERVED
|
||||||
|
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
|
||||||
|
'\ufffe', // 0x61 -> COMMODORE SYMBOL
|
||||||
|
'\u2191', // ↑ 0x62 -> UP ARROW
|
||||||
|
'\u2193', // ↓ 0x63 -> DOWN ARROW
|
||||||
|
'\u2190', // ← 0x64 -> LEFT ARROW
|
||||||
|
'\u2192', // → 0x65 -> RIGHT ARROW
|
||||||
|
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
|
||||||
|
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
|
||||||
|
'\u2026', // … 0x68 -> ELLIPSIS
|
||||||
|
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
|
||||||
|
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
|
||||||
|
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
|
||||||
|
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
|
||||||
|
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
|
||||||
|
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
|
||||||
|
'\ufffe', // 0x6F -> UTILITY TITLE BAR
|
||||||
|
'\u00a9', // © 0x70 -> COPYRIGHT
|
||||||
|
'\u2713', // ✓ 0x71 -> CHECKMARK
|
||||||
|
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
|
||||||
|
'\ufffe', // 0x73 -> TICK TRACK
|
||||||
|
'\ufffe', // 0x74 -> TICK TRACK NUB
|
||||||
|
'\ufffe', // 0x75 -> TAB CORNER
|
||||||
|
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
|
||||||
|
'\ufffe', // 0x77 -> CUSTOM 1
|
||||||
|
'\ufffe', // 0x78 -> CUSTOM 2
|
||||||
|
'\ufffe', // 0x79 -> CUSTOM 3
|
||||||
|
'\ufffe', // 0x7A -> CUSTOM 4
|
||||||
|
'\ufffe', // 0x7B -> CUSTOM 5
|
||||||
|
'\ufffe', // 0x7C -> CUSTOM 6
|
||||||
|
'\ufffe', // 0x7D -> CUSTOM 7
|
||||||
|
'\ufffe', // 0x7E -> CUSTOM 8
|
||||||
|
'\ufffe', // 0x7F -> CUSTOM 9
|
||||||
|
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
|
||||||
|
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
|
||||||
|
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
|
||||||
|
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
|
||||||
|
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
|
||||||
|
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
|
||||||
|
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
|
||||||
|
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
|
||||||
|
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
|
||||||
|
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
|
||||||
|
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
|
||||||
|
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
|
||||||
|
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
|
||||||
|
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
|
||||||
|
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
|
||||||
|
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
|
||||||
|
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
|
||||||
|
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
|
||||||
|
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
|
||||||
|
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
|
||||||
|
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
|
||||||
|
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
|
||||||
|
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
|
||||||
|
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
|
||||||
|
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
|
||||||
|
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
|
||||||
|
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
|
||||||
|
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
|
||||||
|
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
|
||||||
|
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
|
||||||
|
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
|
||||||
|
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
|
||||||
|
'\ufffe', // 0xA0 -> REVERSED SPACE
|
||||||
|
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
|
||||||
|
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
|
||||||
|
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
|
||||||
|
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
|
||||||
|
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
|
||||||
|
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
|
||||||
|
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
|
||||||
|
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
|
||||||
|
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
|
||||||
|
'\ufffe', // 0xAA -> REVERSED ASTERISK
|
||||||
|
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
|
||||||
|
'\ufffe', // 0xAC -> REVERSED COMMA
|
||||||
|
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
|
||||||
|
'\ufffe', // 0xAE -> REVERSED FULL STOP
|
||||||
|
'\ufffe', // 0xAF -> REVERSED SOLIDUS
|
||||||
|
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
|
||||||
|
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
|
||||||
|
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
|
||||||
|
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
|
||||||
|
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
|
||||||
|
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
|
||||||
|
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
|
||||||
|
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
|
||||||
|
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
|
||||||
|
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
|
||||||
|
'\ufffe', // 0xBA -> REVERSED COLON
|
||||||
|
'\ufffe', // 0xBB -> REVERSED SEMICOLON
|
||||||
|
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
|
||||||
|
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
|
||||||
|
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
|
||||||
|
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
|
||||||
|
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
|
||||||
|
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
|
||||||
|
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
|
||||||
|
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
|
||||||
|
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
|
||||||
|
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
|
||||||
|
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
|
||||||
|
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
|
||||||
|
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
|
||||||
|
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
|
||||||
|
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
|
||||||
|
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
|
||||||
|
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
|
||||||
|
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
|
||||||
|
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
|
||||||
|
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
|
||||||
|
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
|
||||||
|
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
|
||||||
|
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
|
||||||
|
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
|
||||||
|
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
|
||||||
|
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
|
||||||
|
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
|
||||||
|
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
|
||||||
|
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
|
||||||
|
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
|
||||||
|
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
|
||||||
|
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
|
||||||
|
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
|
||||||
|
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
|
||||||
|
'\ufffe', // 0xDE -> REVERSED TILDE
|
||||||
|
'\ufffe', // 0xDF -> RESERVED
|
||||||
|
'\ufffe', // 0xE0 -> RESERVED
|
||||||
|
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
|
||||||
|
'\ufffe', // 0xE2 -> REVERSED UP ARROW
|
||||||
|
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
|
||||||
|
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
|
||||||
|
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
|
||||||
|
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
|
||||||
|
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
|
||||||
|
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
|
||||||
|
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
|
||||||
|
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
|
||||||
|
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
|
||||||
|
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
|
||||||
|
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
|
||||||
|
'\ufffe', // 0xEE -> MEMORY CHIP ICON
|
||||||
|
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
|
||||||
|
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
|
||||||
|
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
|
||||||
|
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
|
||||||
|
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
|
||||||
|
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
|
||||||
|
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
|
||||||
|
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
|
||||||
|
'\ufffe', // 0xF7 -> CUSTOM 10
|
||||||
|
'\ufffe', // 0xF8 -> CUSTOM 11
|
||||||
|
'\ufffe', // 0xF9 -> CUSTOM 12
|
||||||
|
'\ufffe', // 0xFA -> CUSTOM 13
|
||||||
|
'\ufffe', // 0xFB -> CUSTOM 14
|
||||||
|
'\ufffe', // 0xFC -> CUSTOM 15
|
||||||
|
'\ufffe', // 0xFD -> CUSTOM 16
|
||||||
|
'\ufffe', // 0xFE -> CUSTOM 17
|
||||||
|
'\ufffe' // 0xFF -> CUSTOM 18
|
||||||
|
)
|
||||||
|
|
||||||
|
// encoding: from unicode to C64 OS Screencodes (0-255)
|
||||||
|
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
|
||||||
|
|
||||||
|
private fun replaceSpecial(chr: Char): Char =
|
||||||
|
when(chr) {
|
||||||
|
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||||
|
else -> chr
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
|
val chr = replaceSpecial(chr3)
|
||||||
|
val screencode = encodingC64os[chr]
|
||||||
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
'\n' -> 13u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if(chr.isISOControl())
|
||||||
|
throw CharConversionException("no c64os character for char #${chr.code}")
|
||||||
|
else
|
||||||
|
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
Ok(text.map {
|
||||||
|
try {
|
||||||
|
encodeChar(it, lowercase)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
encodeChar(it, !lowercase)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch(cx: CharConversionException) {
|
||||||
|
Err(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(screencode.map {
|
||||||
|
val code = it.toInt()
|
||||||
|
if(code<0 || code>= decodingC64os.size)
|
||||||
|
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
|
||||||
|
decodingC64os[code]
|
||||||
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,24 @@
|
|||||||
package prog8.code.target
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.Encoding
|
||||||
import prog8.code.core.IStringEncoding
|
import prog8.code.core.IStringEncoding
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
import prog8.code.target.encodings.*
|
|
||||||
|
|
||||||
|
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||||
object Encoder: IStringEncoding {
|
|
||||||
override val defaultEncoding: Encoding = Encoding.ISO
|
override val defaultEncoding: Encoding = Encoding.ISO
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
val coded = when(encoding) {
|
val coded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||||
Encoding.ISO -> IsoEncoding.encode(str)
|
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||||
|
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
return coded.fold(
|
return coded.fold(
|
||||||
@ -31,12 +30,13 @@ object Encoder: IStringEncoding {
|
|||||||
val decoded = when(encoding) {
|
val decoded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||||
|
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
return decoded.fold(
|
return decoded.fold(
|
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
|||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
open class IsoEncodingBase(charsetName: String) {
|
open class IsoEncodingBase(charsetName: String) {
|
||||||
val charset: Charset = Charset.forName(charsetName)
|
val charset: Charset = Charset.forName(charsetName)
|
||||||
|
|
||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||||
return try {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(chr.code - 0x8000).toUByte()
|
||||||
@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||||
return try {
|
return try {
|
||||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
Ok(String(bytes.map {
|
||||||
|
when(it) {
|
||||||
|
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||||
|
else -> it.toByte()
|
||||||
|
}
|
||||||
|
}.toByteArray(), charset))
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
|||||||
object KatakanaEncoding {
|
object KatakanaEncoding {
|
||||||
val charset: Charset = Charset.forName("JIS_X0201")
|
val charset: Charset = Charset.forName("JIS_X0201")
|
||||||
|
|
||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||||
return try {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
|
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||||
@ -112,9 +113,14 @@ object KatakanaEncoding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||||
return try {
|
return try {
|
||||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
Ok(String(bytes.map {
|
||||||
|
when(it) {
|
||||||
|
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||||
|
else -> it.toByte()
|
||||||
|
}
|
||||||
|
}.toByteArray(), charset))
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import java.io.CharConversionException
|
|||||||
|
|
||||||
object PetsciiEncoding {
|
object PetsciiEncoding {
|
||||||
|
|
||||||
// decoding: from Petscii/Screencodes (0-255) to unicode
|
// decoding: from Petscii/Screencodes (0-255) to Unicode
|
||||||
// character tables used from https://github.com/irmen/cbmcodecs2
|
// character tables used from https://github.com/irmen/cbmcodecs2
|
||||||
|
|
||||||
private val decodingPetsciiLowercase = charArrayOf(
|
private val decodingPetsciiLowercase = charArrayOf(
|
||||||
@ -21,7 +21,7 @@ object PetsciiEncoding {
|
|||||||
'\ufffe', // 0x07 -> UNDEFINED
|
'\ufffe', // 0x07 -> UNDEFINED
|
||||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\ufffe', // 0x0A -> UNDEFINED
|
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||||
'\ufffe', // 0x0B -> UNDEFINED
|
'\ufffe', // 0x0B -> UNDEFINED
|
||||||
'\ufffe', // 0x0C -> UNDEFINED
|
'\ufffe', // 0x0C -> UNDEFINED
|
||||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||||
@ -1089,7 +1089,7 @@ object PetsciiEncoding {
|
|||||||
Ok(text.map {
|
Ok(text.map {
|
||||||
try {
|
try {
|
||||||
encodeChar(it, lowercase)
|
encodeChar(it, lowercase)
|
||||||
} catch (x: CharConversionException) {
|
} catch (_: CharConversionException) {
|
||||||
encodeChar(it, !lowercase)
|
encodeChar(it, !lowercase)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
|||||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
return screencode?.toUByte() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> 141u
|
||||||
|
'\r' -> 141u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(chr.code - 0x8000).toUByte()
|
||||||
@ -1135,7 +1137,7 @@ object PetsciiEncoding {
|
|||||||
Ok(text.map {
|
Ok(text.map {
|
||||||
try {
|
try {
|
||||||
encodeChar(it, lowercase)
|
encodeChar(it, lowercase)
|
||||||
} catch (x: CharConversionException) {
|
} catch (_: CharConversionException) {
|
||||||
encodeChar(it, !lowercase)
|
encodeChar(it, !lowercase)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1157,16 +1159,16 @@ object PetsciiEncoding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
||||||
val code: UInt = when {
|
val code: UInt = when {
|
||||||
petscii_code <= 0x1fu -> petscii_code + 128u
|
petsciicode <= 0x1fu -> petsciicode + 128u
|
||||||
petscii_code <= 0x3fu -> petscii_code.toUInt()
|
petsciicode <= 0x3fu -> petsciicode.toUInt()
|
||||||
petscii_code <= 0x5fu -> petscii_code - 64u
|
petsciicode <= 0x5fu -> petsciicode - 64u
|
||||||
petscii_code <= 0x7fu -> petscii_code - 32u
|
petsciicode <= 0x7fu -> petsciicode - 32u
|
||||||
petscii_code <= 0x9fu -> petscii_code + 64u
|
petsciicode <= 0x9fu -> petsciicode + 64u
|
||||||
petscii_code <= 0xbfu -> petscii_code - 64u
|
petsciicode <= 0xbfu -> petsciicode - 64u
|
||||||
petscii_code <= 0xfeu -> petscii_code - 128u
|
petsciicode <= 0xfeu -> petsciicode - 128u
|
||||||
petscii_code == 255.toUByte() -> 95u
|
petsciicode == 255.toUByte() -> 95u
|
||||||
else -> return Err(CharConversionException("petscii code out of range"))
|
else -> return Err(CharConversionException("petscii code out of range"))
|
||||||
}
|
}
|
||||||
if(inverseVideo) {
|
if(inverseVideo) {
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
package prog8.code.target.pet
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.cbm.Mflpt5
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class PETMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
|
||||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
|
||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
|
||||||
|
|
||||||
override val BSSHIGHRAM_START = 0u
|
|
||||||
override val BSSHIGHRAM_END = 0u
|
|
||||||
override val BSSGOLDENRAM_START = 0u
|
|
||||||
override val BSSGOLDENRAM_END = 0u
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val m5 = Mflpt5.fromNumber(num)
|
|
||||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==5) { "need 5 bytes" }
|
|
||||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
|
||||||
return m5.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
if(selectedEmulator!=1) {
|
|
||||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\nStarting PET emulator...")
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
|
||||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process=processb.start()
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = PETZeropage(compilerOptions)
|
|
||||||
// there's no golden ram.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
package prog8.code.target.virtual
|
|
||||||
|
|
||||||
import prog8.code.core.*
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.isReadable
|
|
||||||
import kotlin.io.path.name
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class VirtualMachineDefinition: IMachineDefinition {
|
|
||||||
|
|
||||||
override val cpu = CpuType.VIRTUAL
|
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
|
||||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
|
||||||
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.toDouble().toBits().toULong()
|
|
||||||
val hexStr = bits.toString(16).padStart(16, '0')
|
|
||||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
|
||||||
return parts.joinToString(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
|
||||||
val bits = num.toBits().toULong()
|
|
||||||
val hexStr = bits.toString(16).padStart(16, '0')
|
|
||||||
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
|
||||||
require(bytes.size==8) { "need 8 bytes" }
|
|
||||||
val b0 = bytes[0].toLong() shl (8*7)
|
|
||||||
val b1 = bytes[1].toLong() shl (8*6)
|
|
||||||
val b2 = bytes[2].toLong() shl (8*5)
|
|
||||||
val b3 = bytes[3].toLong() shl (8*4)
|
|
||||||
val b4 = bytes[4].toLong() shl (8*3)
|
|
||||||
val b5 = bytes[5].toLong() shl (8*2)
|
|
||||||
val b6 = bytes[6].toLong() shl (8*1)
|
|
||||||
val b7 = bytes[7].toLong() shl (8*0)
|
|
||||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
|
||||||
println("\nStarting Virtual Machine...")
|
|
||||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
|
||||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
|
||||||
val filename = programNameWithPath.name
|
|
||||||
if(programNameWithPath.isReadable()) {
|
|
||||||
vm.runProgram(programNameWithPath.readText())
|
|
||||||
} else {
|
|
||||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
|
||||||
if(withExt.isReadable())
|
|
||||||
vm.runProgram(withExt.readText())
|
|
||||||
else
|
|
||||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = false
|
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
|
||||||
zeropage = VirtualZeropage(compilerOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IVirtualMachineRunner {
|
|
||||||
fun runProgram(irSource: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
|
||||||
override val SCRATCH_B1: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_REG: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_W1: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_W2: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.c128
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
@ -6,22 +6,16 @@ import prog8.code.core.Zeropage
|
|||||||
import prog8.code.core.ZeropageType
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
// reference: "Mapping the C128" zero page chapter.
|
// reference: "Mapping the C128" zeropage chapter.
|
||||||
|
|
||||||
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
|
||||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
|
||||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
|
||||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -33,18 +27,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
ZeropageType.FULL -> {
|
ZeropageType.FULL -> {
|
||||||
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
||||||
free.addAll(0x0au..0xffu)
|
free.addAll(0x0au..0xffu)
|
||||||
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
|
free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
|
||||||
}
|
}
|
||||||
ZeropageType.KERNALSAFE -> {
|
ZeropageType.KERNALSAFE -> {
|
||||||
free.addAll(0x0au..0x8fu) // BASIC variables
|
free.addAll(0x0au..0x8fu) // BASIC variables
|
||||||
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||||
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
||||||
}
|
}
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE -> {
|
ZeropageType.BASICSAFE -> {
|
||||||
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
||||||
free.addAll(0x1bu..0x23u)
|
free.addAll(0x1bu..0x23u)
|
||||||
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
|
free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
|
||||||
0x55u, 0x56u, 0x57u, 0x58u,
|
0x55u, 0x56u, 0x57u, 0x58u,
|
||||||
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
|
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
|
||||||
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||||
@ -53,7 +47,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
|
|
||||||
// if(options.zeropage==ZeropageType.BASICSAFE) {
|
// if(options.zeropage==ZeropageType.BASICSAFE) {
|
||||||
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
|
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
|
||||||
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||||
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||||
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||||
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||||
@ -71,8 +65,4 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if C128 can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.c64
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
|
|
||||||
if (options.zeropage == ZeropageType.FULL) {
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
free.addAll(0x02u..0xffu)
|
free.addAll(0x02u..0xffu)
|
||||||
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||||
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
free.addAll(listOf(
|
free.addAll(arrayOf(
|
||||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||||
@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
|
|
||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zeropage locations used for floating point operations from the free list
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(arrayOf(
|
||||||
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
||||||
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
@ -56,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
if(options.zeropage != ZeropageType.DONTUSE) {
|
if(options.zeropage != ZeropageType.DONTUSE) {
|
||||||
// add the free Zp addresses
|
// add the free Zp addresses
|
||||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||||
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||||
} else {
|
} else {
|
||||||
@ -79,9 +79,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
private fun allocateCx16VirtualRegisters() {
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.cx16
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
private fun allocateCx16VirtualRegisters() {
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
class ConfigurableZeropage(
|
||||||
|
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||||
|
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||||
|
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||||
|
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||||
|
basicsafe: List<UIntRange>,
|
||||||
|
kernalsafe: List<UIntRange>,
|
||||||
|
fullsafe: List<UIntRange>,
|
||||||
|
options: CompilationOptions
|
||||||
|
) : Zeropage(options) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
TODO("floats in configurable target zp")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||||
|
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
|
||||||
|
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||||
|
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||||
|
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||||
|
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||||
|
}
|
||||||
|
|
||||||
|
val distinctFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
allocateCx16VirtualRegisters()
|
||||||
|
retainAllowed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allocateCx16VirtualRegisters() {
|
||||||
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
|
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||||
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
|
for(reg in 0..15) {
|
||||||
|
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||||
|
if(address<=0xffu) {
|
||||||
|
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.pet
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
@ -11,15 +11,11 @@ import prog8.code.core.ZeropageType
|
|||||||
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -52,8 +48,4 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
retainAllowed()
|
retainAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocateCx16VirtualRegisters() {
|
|
||||||
TODO("Not known if PET can put the virtual regs in ZP")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,63 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
id "org.jetbrains.kotlin.jvm"
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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:2.0.0"
|
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
|
|
||||||
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
java {
|
|
||||||
srcDir "${project.projectDir}/src"
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
srcDir "${project.projectDir}/res"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test {
|
|
||||||
java {
|
|
||||||
srcDir "${project.projectDir}/test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
// Enable JUnit 5 (Gradle 4.6+).
|
|
||||||
useJUnitPlatform()
|
|
||||||
|
|
||||||
// Always run tests, even when nothing changed.
|
|
||||||
dependsOn 'cleanTest'
|
|
||||||
|
|
||||||
// Show test results.
|
|
||||||
testLogging {
|
|
||||||
events "skipped", "failed"
|
|
||||||
}
|
|
||||||
}
|
|
47
codeGenCpu6502/build.gradle.kts
Normal file
47
codeGenCpu6502/build.gradle.kts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":codeCore"))
|
||||||
|
implementation(project(":simpleAst"))
|
||||||
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
|
|
||||||
|
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||||
|
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
setSrcDirs(listOf("$projectDir/src"))
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
setSrcDirs(listOf("$projectDir/res"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
setSrcDirs(listOf("$projectDir/test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn("cleanTest")
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events("skipped", "failed")
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
|
<orderEntry type="module" module-name="simpleAst" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,16 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.GENERATED_LABEL_PREFIX
|
||||||
import prog8.code.StConstant
|
import prog8.code.StConstant
|
||||||
import prog8.code.StMemVar
|
import prog8.code.StMemVar
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.core.IMachineDefinition
|
import prog8.code.core.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
||||||
|
|
||||||
var numberOfOptimizations = 0
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
|||||||
|
|
||||||
private fun optimizeSameAssignments(
|
private fun optimizeSameAssignments(
|
||||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||||
machine: IMachineDefinition,
|
machine: ICompilationTarget,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
|
|
||||||
@ -361,15 +362,16 @@ or *_afterif labels.
|
|||||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
val autoLabelPrefix = GENERATED_LABEL_PREFIX
|
||||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||||
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
|
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
}
|
}
|
||||||
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
else if(fourth.startsWith(autoLabelPrefix) && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||||
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
|
if((fifth.startsWith("beq $autoLabelPrefix") || fifth.startsWith("bne $autoLabelPrefix")) &&
|
||||||
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
@ -384,7 +386,7 @@ This gets generated after certain if conditions, and only the branch instruction
|
|||||||
|
|
||||||
private fun optimizeStoreLoadSame(
|
private fun optimizeStoreLoadSame(
|
||||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||||
machine: IMachineDefinition,
|
machine: ICompilationTarget,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -457,7 +459,7 @@ private fun optimizeStoreLoadSame(
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||||
|
|
||||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
// try to get the constant value address, could return null if it's a symbol instead
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
@ -506,9 +508,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
|||||||
|
|
||||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// jsr Sub + rts -> jmp Sub
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
// jmp Sub + rts -> jmp Sub
|
||||||
// rts + jmp -> remove jmp
|
// rts + jmp -> remove jmp
|
||||||
// rts + bxx -> remove bxx
|
// rts + bxx -> remove bxx
|
||||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||||
|
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||||
// and some other optimizations.
|
// and some other optimizations.
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -516,34 +520,40 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
|||||||
val first = lines[0].value
|
val first = lines[0].value
|
||||||
val second = lines[1].value
|
val second = lines[1].value
|
||||||
val third = lines[2].value
|
val third = lines[2].value
|
||||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
|
||||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
}
|
|
||||||
else if (" rts" in first || "\trts" in first) {
|
|
||||||
if (" jmp" in second || "\tjmp" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bra" in second || "\tbra" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bcc" in second || "\tbcc" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bcs" in second || "\tbcs" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" beq" in second || "\tbeq" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bne" in second || "\tbne" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bmi" in second || "\tbmi" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bpl" in second || "\tbpl" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bvs" in second || "\tbvs" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bvc" in second || "\tbvc" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!haslabel(second)) {
|
if(!haslabel(second)) {
|
||||||
|
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
}
|
||||||
|
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||||
|
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||||
|
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (" rts" in first || "\trts" in first) {
|
||||||
|
if (" jmp" in second || "\tjmp" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bra" in second || "\tbra" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcc" in second || "\tbcc" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcs" in second || "\tbcs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" beq" in second || "\tbeq" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bne" in second || "\tbne" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bmi" in second || "\tbmi" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bpl" in second || "\tbpl" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvs" in second || "\tbvs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvc" in second || "\tbvc" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
}
|
||||||
|
|
||||||
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in 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) ||
|
(" 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)
|
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
|
||||||
@ -558,6 +568,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only remove bra followed by jmp or jmp followed by bra
|
||||||
|
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||||
|
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
|
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -682,18 +701,20 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
|||||||
// phy + ldy + pla -> tya + ldy
|
// phy + ldy + pla -> tya + ldy
|
||||||
// phx + ldx + pla -> txa + ldx
|
// phx + ldx + pla -> txa + ldx
|
||||||
// pha + lda + pla -> nop
|
// pha + lda + pla -> nop
|
||||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
when (first) {
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " tya"))
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
}
|
mods.add(Modification(lines[1].index, false, " tya"))
|
||||||
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
}
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " txa"))
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
}
|
mods.add(Modification(lines[1].index, false, " txa"))
|
||||||
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
}
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,18 +11,22 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
|||||||
// 1) cx16 virtual word registers,
|
// 1) cx16 virtual word registers,
|
||||||
// 2) paired CPU registers,
|
// 2) paired CPU registers,
|
||||||
// 3) single CPU registers (order Y,X,A),
|
// 3) single CPU registers (order Y,X,A),
|
||||||
// 4) CPU Carry status flag
|
// 4) floating point registers (FAC1, FAC2),
|
||||||
|
// 5) CPU Carry status flag
|
||||||
val args = sub.parameters.withIndex()
|
val args = sub.parameters.withIndex()
|
||||||
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||||
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||||
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null }
|
val (singleRegsMixed, rest) = args3.partition { it.value.first.registerOrPair != null }
|
||||||
|
val (singleCpuRegs, floatRegs) = singleRegsMixed.partition {it.value.first.registerOrPair != RegisterOrPair.FAC1 && it.value.first.registerOrPair != RegisterOrPair.FAC2 }
|
||||||
|
|
||||||
cx16regs.forEach { order += it.index }
|
cx16regs.forEach { order += it.index }
|
||||||
pairedRegs.forEach { order += it.index }
|
pairedRegs.forEach { order += it.index }
|
||||||
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
singleCpuRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
||||||
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
|
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
|
||||||
|
floatRegs.forEach { order += it.index }
|
||||||
rest.forEach { order += it.index }
|
rest.forEach { order += it.index }
|
||||||
require(order.size==sub.parameters.size)
|
require(order.size==sub.parameters.size)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.GENERATED_LABEL_PREFIX
|
||||||
|
import prog8.code.IAssemblyProgram
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.code.core.OutputType
|
||||||
|
import prog8.code.target.C128Target
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
|
import prog8.code.target.PETTarget
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
@ -21,15 +28,12 @@ internal class AssemblyProgram(
|
|||||||
|
|
||||||
val assemblerCommand: List<String>
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
when (compTarget.name) {
|
when(options.output) {
|
||||||
in setOf("c64", "c128", "cx16", "pet32") -> {
|
OutputType.PRG -> {
|
||||||
// CBM machines .prg generation.
|
// CBM machines .prg generation.
|
||||||
|
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
|
||||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
if(options.warnSymbolShadowing)
|
||||||
command.add("-Wshadow")
|
command.add("-Wshadow")
|
||||||
@ -40,34 +44,19 @@ internal class AssemblyProgram(
|
|||||||
command.add("--quiet")
|
command.add("--quiet")
|
||||||
|
|
||||||
if(options.asmListfile) {
|
if(options.asmListfile) {
|
||||||
command.addAll(listOf("--list=$listFile", "--no-monitor"))
|
command.add("--list=$listFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||||
OutputType.PRG -> {
|
|
||||||
command.add("--cbm-prg")
|
|
||||||
println("\nCreating prg for target ${compTarget.name}.")
|
|
||||||
prgFile
|
|
||||||
}
|
|
||||||
OutputType.RAW -> {
|
|
||||||
command.add("--nostart")
|
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
|
||||||
binFile
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid output type")
|
|
||||||
}
|
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
}
|
}
|
||||||
"atari" -> {
|
OutputType.XEX -> {
|
||||||
// Atari800XL .xex generation.
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
// TODO are these options okay for atari?
|
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
|
||||||
"--no-monitor"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
if(options.warnSymbolShadowing)
|
||||||
command.add("-Wshadow")
|
command.add("-Wshadow")
|
||||||
@ -80,28 +69,73 @@ internal class AssemblyProgram(
|
|||||||
if(options.asmListfile)
|
if(options.asmListfile)
|
||||||
command.add("--list=$listFile")
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||||
OutputType.XEX -> {
|
assemblerCommand = command
|
||||||
command.add("--atari-xex")
|
if(!options.quiet)
|
||||||
println("\nCreating xex for target ${compTarget.name}.")
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
xexFile
|
}
|
||||||
}
|
OutputType.RAW -> {
|
||||||
OutputType.RAW -> {
|
// Neo6502/headerless raw program generation.
|
||||||
command.add("--nostart")
|
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
binFile
|
|
||||||
}
|
if(options.warnSymbolShadowing)
|
||||||
else -> throw AssemblyError("invalid output type")
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
|
}
|
||||||
|
OutputType.LIBRARY -> {
|
||||||
|
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
||||||
|
|
||||||
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
|
|
||||||
|
if(options.warnSymbolShadowing)
|
||||||
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||||
|
command.add("--cbm-prg")
|
||||||
|
} else {
|
||||||
|
if(!options.quiet)
|
||||||
|
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||||
|
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||||
}
|
}
|
||||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid compilation target")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||||
val result = proc.waitFor()
|
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||||
if (result == 0 && compTarget.name!="atari") {
|
|
||||||
|
val proc = ProcessBuilder(assemblerCommand)
|
||||||
|
if(!options.quiet)
|
||||||
|
proc.inheritIO()
|
||||||
|
val process = proc.start()
|
||||||
|
val result = process.waitFor()
|
||||||
|
if (result == 0) {
|
||||||
removeGeneratedLabelsFromMonlist()
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
@ -109,7 +143,7 @@ internal class AssemblyProgram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
|
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
|
||||||
val lines = viceMonListFile.toFile().readLines()
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
@ -126,7 +160,7 @@ internal class AssemblyProgram(
|
|||||||
for (line in viceMonListFile.toFile().readLines()) {
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if (match != null)
|
if (match != null)
|
||||||
breakpoints.add("break \$" + match.groupValues[1])
|
breakpoints.add("break $" + match.groupValues[1])
|
||||||
}
|
}
|
||||||
val num = breakpoints.size
|
val num = breakpoints.size
|
||||||
breakpoints.add(0, "; breakpoint list now follows")
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,40 +0,0 @@
|
|||||||
package prog8.codegen.cpu6502
|
|
||||||
|
|
||||||
import prog8.code.ast.IPtSubroutine
|
|
||||||
import prog8.code.ast.PtAsmSub
|
|
||||||
import prog8.code.ast.PtSub
|
|
||||||
import prog8.code.core.*
|
|
||||||
|
|
||||||
|
|
||||||
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
|
||||||
when(this) {
|
|
||||||
is PtAsmSub -> {
|
|
||||||
return returns
|
|
||||||
}
|
|
||||||
is PtSub -> {
|
|
||||||
// for non-asm subroutines, determine the return registers based on the type of the return value
|
|
||||||
return if(returntype==null)
|
|
||||||
emptyList()
|
|
||||||
else {
|
|
||||||
val register = when (returntype!!) {
|
|
||||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
|
||||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
|
||||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
}
|
|
||||||
listOf(Pair(register, returntype!!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
|
||||||
return when(returntype) {
|
|
||||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
|
||||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
|
||||||
null -> null
|
|
||||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,224 +33,351 @@ internal class ForLoopsAsmGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
if(range.step.asConstInteger()!! < -1) {
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
|
||||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
|
||||||
asmgen.loopEndLabels.add(endLabel)
|
|
||||||
val stepsize=range.step.asConstInteger()!!
|
|
||||||
|
|
||||||
if(stepsize < -1) {
|
|
||||||
val limit = range.to.asConstInteger()
|
val limit = range.to.asConstInteger()
|
||||||
if(limit==0)
|
if(limit==0)
|
||||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(iterableDt) {
|
when {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
}
|
||||||
// simple loop downto 0 step -1
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.loopEndLabels.removeLast()
|
||||||
asmgen.translate(stmt.statements)
|
}
|
||||||
|
|
||||||
|
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.add(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
|
|
||||||
|
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||||
|
when (stepsize) {
|
||||||
|
-1 if range.to.asConstInteger()==0 -> {
|
||||||
|
// simple loop downto 0 step -1
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
dec $varname
|
||||||
|
lda $varname
|
||||||
|
cmp #255
|
||||||
|
bne $loopLabel""")
|
||||||
|
}
|
||||||
|
-1 if range.to.asConstInteger()==1 -> {
|
||||||
|
// simple loop downto 1 step -1
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
dec $varname
|
||||||
|
bne $loopLabel""")
|
||||||
|
}
|
||||||
|
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// bytes range, step 1 or -1
|
||||||
|
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
|
|
||||||
|
if(asmgen.options.romable) {
|
||||||
|
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||||
|
// so we need to store the loop end value in a newly allocated temporary variable
|
||||||
|
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||||
|
asmgen.out(" sta $toValueVar")
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt.isSignedByteArray) {
|
||||||
|
if(stepsize<0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
clc
|
||||||
lda $varname
|
sbc $varname
|
||||||
cmp #255
|
bvc +
|
||||||
bne $loopLabel""")
|
eor #$80
|
||||||
|
+ bpl $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else {
|
||||||
// simple loop downto 1 step -1
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
sec
|
||||||
bne $loopLabel""")
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==1 || stepsize==-1) {
|
} else {
|
||||||
// bytes array, step 1 or -1
|
if(stepsize<0) {
|
||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
|
||||||
// loop over byte range via loopvar
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
|
||||||
// pre-check for end already reached
|
|
||||||
if(iterableDt==DataType.ARRAY_B) {
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
if(stepsize<0) {
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bpl $endLabel""")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bmi $endLabel""")
|
|
||||||
} else {
|
|
||||||
if(stepsize<0) {
|
|
||||||
asmgen.out("""
|
|
||||||
cmp $varname
|
|
||||||
beq +
|
|
||||||
bcs $endLabel
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
}
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
cmp $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
beq +
|
||||||
beq $endLabel
|
bcs $endLabel
|
||||||
$incdec $varname""")
|
|
||||||
asmgen.jmp(loopLabel)
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// bytes, step >= 2 or <= -2
|
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
|
||||||
// pre-check for end already reached
|
|
||||||
if(iterableDt==DataType.ARRAY_B) {
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
if(stepsize<0)
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bpl $endLabel""")
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bmi $endLabel""")
|
|
||||||
} else {
|
|
||||||
if(stepsize<0)
|
|
||||||
asmgen.out("""
|
|
||||||
cmp $varname
|
|
||||||
beq +
|
|
||||||
bcs $endLabel
|
|
||||||
+""")
|
+""")
|
||||||
else
|
}
|
||||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
else {
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
}
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
if(stepsize>0) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
clc
|
|
||||||
adc #$stepsize
|
|
||||||
sta $varname
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bmi $loopLabel
|
|
||||||
beq $loopLabel""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
sec
|
|
||||||
sbc #${stepsize.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bpl $loopLabel""")
|
|
||||||
}
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
asmgen.out(loopLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
asmgen.translate(forloop.statements)
|
||||||
assignLoopvarWord(stmt, range)
|
asmgen.out("""
|
||||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
lda $varname
|
||||||
// simple loop downto 0 step -1 (words)
|
cmp $toValueVar
|
||||||
asmgen.out(loopLabel)
|
beq $endLabel
|
||||||
asmgen.translate(stmt.statements)
|
$incdec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// use self-modifying code to store the loop end comparison value
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt.isSignedByteArray) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
clc
|
||||||
bne ++
|
sbc $varname
|
||||||
lda $varname+1
|
bvc +
|
||||||
beq $endLabel
|
eor #$80
|
||||||
+ lda $varname
|
+ bpl $endLabel""")
|
||||||
bne +
|
|
||||||
dec $varname+1
|
|
||||||
+ dec $varname""")
|
|
||||||
asmgen.jmp(loopLabel)
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else
|
||||||
// simple loop downto 1 step -1 (words)
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
sec
|
||||||
cmp #1
|
sbc $varname
|
||||||
bne +
|
bvc +
|
||||||
lda $varname+1
|
eor #$80
|
||||||
beq $endLabel
|
+ bmi $endLabel""")
|
||||||
+ lda $varname
|
} else {
|
||||||
bne +
|
if(stepsize<0) {
|
||||||
dec $varname+1
|
asmgen.out("""
|
||||||
+ dec $varname""")
|
cmp $varname
|
||||||
asmgen.jmp(loopLabel)
|
beq +
|
||||||
asmgen.out(endLabel)
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
else if (stepsize == 1 || stepsize == -1) {
|
else {
|
||||||
// words, step 1 or -1
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
}
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
asmgen.out("""
|
}
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
beq $endLabel
|
||||||
|
$incdec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// bytes range, step >= 2 or <= -2
|
||||||
|
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt.isSignedByteArray) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi $endLabel""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varname
|
||||||
|
beq +
|
||||||
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
|
else {
|
||||||
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
|
}
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
}
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
|
||||||
|
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
|
||||||
|
if(stepsize>0) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
clc
|
||||||
|
adc #$stepsize
|
||||||
|
sta $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
bmi $loopLabel
|
||||||
|
beq $loopLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
sec
|
||||||
|
sbc #${stepsize.absoluteValue}
|
||||||
|
sta $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
bpl $loopLabel""")
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.add(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
|
assignLoopvarWord(stmt, range)
|
||||||
|
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||||
|
// simple loop downto 0 step -1 (words)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
bne ++
|
||||||
|
lda $varname+1
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||||
|
// simple loop downto 1 step -1 (words)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #1
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
else if (stepsize == 1 || stepsize == -1)
|
||||||
|
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else if (stepsize > 0)
|
||||||
|
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else
|
||||||
|
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// words range, step 1 or -1
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
|
||||||
|
if(asmgen.options.romable) {
|
||||||
|
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||||
|
// so we need to store the loop end value in a newly allocated temporary variable
|
||||||
|
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out(" sta $toValueVar")
|
||||||
|
asmgen.out(" sty $toValueVar+1")
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname+1
|
||||||
|
cmp $toValueVar+1
|
||||||
|
bne +
|
||||||
|
lda $varname
|
||||||
|
cmp $toValueVar
|
||||||
|
beq $endLabel""")
|
||||||
|
if(stepsize==1) {
|
||||||
|
asmgen.out("""
|
||||||
|
+ inc $varname
|
||||||
|
bne $loopLabel
|
||||||
|
inc $varname+1""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(forloop.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel2 cmp #0 ; modified
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
if(stepsize==1) {
|
if(stepsize==1) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1""")
|
inc $varname+1""")
|
||||||
asmgen.jmp(loopLabel)
|
asmgen.jmp(loopLabel)
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname""")
|
+ dec $varname""")
|
||||||
asmgen.jmp(loopLabel)
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
else if (stepsize > 0) {
|
}
|
||||||
// (u)words, step >= 2
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
// (u)words, step >= 2
|
||||||
asmgen.out("""
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if (iterableDt == DataType.ARRAY_UW) {
|
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||||
asmgen.out("""
|
if (iterableDt.isUnsignedWordArray) {
|
||||||
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -266,8 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
|||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bcs $loopLabel
|
bcs $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -283,20 +410,22 @@ $modifiedLabel lda #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
|
||||||
// (u)words, step <= -2
|
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
// (u)words, step <= -2
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
val stepsize = range.step.asConstInteger()!!
|
||||||
asmgen.out("""
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
lda $varname
|
||||||
sec
|
sec
|
||||||
sbc #<${stepsize.absoluteValue}
|
sbc #<${stepsize.absoluteValue}
|
||||||
@ -313,40 +442,35 @@ $modifiedLabel sbc #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable
|
||||||
}
|
|
||||||
else -> throw AssemblyError("range expression can only be byte or word")
|
|
||||||
}
|
|
||||||
|
|
||||||
asmgen.loopEndLabels.removeLast()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||||
// pre-check for end already reached.
|
// pre-check for end already reached.
|
||||||
// 'to' is in AY, do NOT clobber this!
|
// 'to' is in AY, do NOT clobber this!
|
||||||
if(iterableDt==DataType.ARRAY_W) {
|
if(iterableDt.isSignedWordArray) {
|
||||||
if(stepsize<0)
|
if(stepsize<0)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_W2 ; to
|
sta P8ZP_SCRATCH_W2 ; to
|
||||||
sty P8ZP_SCRATCH_W2+1 ; to
|
sty P8ZP_SCRATCH_W2+1 ; to
|
||||||
lda $fromVar
|
lda $fromVar
|
||||||
cmp P8ZP_SCRATCH_W2
|
cmp P8ZP_SCRATCH_W2
|
||||||
lda $fromVar+1
|
lda $fromVar+1
|
||||||
sbc P8ZP_SCRATCH_W2+1
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
ldy P8ZP_SCRATCH_W2+1""")
|
ldy P8ZP_SCRATCH_W2+1""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
tya
|
tya
|
||||||
sbc $fromVar+1
|
sbc $fromVar+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_REG""")
|
lda P8ZP_SCRATCH_REG""")
|
||||||
} else {
|
} else {
|
||||||
if(stepsize<0)
|
if(stepsize<0)
|
||||||
@ -362,11 +486,11 @@ $endLabel""")
|
|||||||
+""")
|
+""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy $fromVar+1
|
cpy $fromVar+1
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bne +
|
bne +
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,26 +505,44 @@ $endLabel""")
|
|||||||
is StMemVar -> symbol.length!!
|
is StMemVar -> symbol.length!!
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
when(iterableDt) {
|
when {
|
||||||
DataType.STR -> {
|
iterableDt.isString -> {
|
||||||
asmgen.out("""
|
if(asmgen.options.romable) {
|
||||||
lda #<$iterableName
|
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
ldy #>$iterableName
|
asmgen.out("""
|
||||||
sta $loopLabel+1
|
ldy #0
|
||||||
sty $loopLabel+2
|
sty $indexVar
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda $iterableName,y
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $loopLabel+1
|
inc $indexVar
|
||||||
bne $loopLabel
|
ldy $indexVar
|
||||||
inc $loopLabel+2
|
bne $loopLabel
|
||||||
bne $loopLabel
|
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
sty $indexVar
|
||||||
|
$loopLabel lda $iterableName,y
|
||||||
|
beq $endLabel
|
||||||
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
inc $indexVar
|
||||||
|
ldy $indexVar
|
||||||
|
bne $loopLabel
|
||||||
|
$indexVar .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
|
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = if(asmgen.options.romable)
|
||||||
|
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||||
|
else
|
||||||
|
asmgen.makeLabel("for_index")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -422,61 +564,25 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
iterableDt.isSplitWordArray -> {
|
||||||
val length = numElements * 2
|
val indexVar = if(asmgen.options.romable)
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
else
|
||||||
asmgen.out("""
|
asmgen.makeLabel("for_index")
|
||||||
ldy #0
|
|
||||||
$loopLabel sty $indexVar
|
|
||||||
lda $iterableName,y
|
|
||||||
sta $loopvarName
|
|
||||||
lda $iterableName+1,y
|
|
||||||
sta $loopvarName+1""")
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
if(length<=127) {
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $indexVar
|
|
||||||
iny
|
|
||||||
iny
|
|
||||||
cpy #$length
|
|
||||||
beq $endLabel
|
|
||||||
bne $loopLabel""")
|
|
||||||
} else {
|
|
||||||
// length is 128 words, 256 bytes
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $indexVar
|
|
||||||
iny
|
|
||||||
iny
|
|
||||||
bne $loopLabel
|
|
||||||
beq $endLabel""")
|
|
||||||
}
|
|
||||||
if(length>=16) {
|
|
||||||
// allocate index var on ZP if possible
|
|
||||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
|
||||||
result.fold(
|
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
asmgen.out("$indexVar .byte 0")
|
|
||||||
}
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -501,19 +607,66 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
iterableDt.isWordArray -> {
|
||||||
|
val indexVar = if(asmgen.options.romable)
|
||||||
|
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
|
else
|
||||||
|
asmgen.makeLabel("for_index")
|
||||||
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $indexVar
|
||||||
|
lda $iterableName,y
|
||||||
|
sta $loopvarName
|
||||||
|
lda $iterableName+1,y
|
||||||
|
sta $loopvarName+1""")
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
if(numElements<=127) {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
cpy #${numElements*2}
|
||||||
|
beq $endLabel
|
||||||
|
bne $loopLabel""")
|
||||||
|
} else {
|
||||||
|
// array size is 128 words, 256 bytes
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
bne $loopLabel
|
||||||
|
beq $endLabel""")
|
||||||
|
}
|
||||||
|
if(!asmgen.options.romable) {
|
||||||
|
if(numElements>=16) {
|
||||||
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
|
result.fold(
|
||||||
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
iterableDt.isFloatArray -> {
|
||||||
throw AssemblyError("for loop with floating point variables is not supported")
|
throw AssemblyError("for loop with floating point variables is not supported")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||||
@ -524,12 +677,12 @@ $loopLabel sty $indexVar
|
|||||||
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
|
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
if (range.isEmpty() || range.step==0)
|
if (range.isEmpty() || range.step==0)
|
||||||
throw AssemblyError("empty range or step 0")
|
throw AssemblyError("empty range or step 0")
|
||||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
if(iterableDt.isByteArray) {
|
||||||
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||||
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||||
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt==DataType.ARRAY_UB)
|
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt.isUnsignedByteArray)
|
||||||
}
|
}
|
||||||
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
|
else if(iterableDt.isWordArray) {
|
||||||
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||||
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||||
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
||||||
@ -539,8 +692,8 @@ $loopLabel sty $indexVar
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.add(endLabel)
|
asmgen.loopEndLabels.add(endLabel)
|
||||||
when(iterableDt) {
|
when {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
iterableDt.isByteArray -> {
|
||||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -605,7 +758,7 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
||||||
// loop over word range via loopvar, step >= 2 or <= -2
|
// loop over word range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
when (range.step) {
|
when (range.step) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.StNodeType
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||||
@ -15,9 +16,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
// just ignore any result values from the function call.
|
// just ignore any result values from the function call.
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>): Boolean {
|
||||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|
// When the parameter(s) are passed via an explicit register or register pair,
|
||||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
|
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||||
|
// Just load them in whatever the register spec says.
|
||||||
|
return when (params.size) {
|
||||||
|
1 -> params[0].type.isIntegerOrBool && params[0].register == null
|
||||||
|
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||||
// Output only the code to set up the parameters and perform the actual call
|
// Output only the code to set up the parameters and perform the actual call
|
||||||
@ -25,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||||
|
|
||||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||||
val sub = symbol?.astNode as IPtSubroutine
|
if(symbol.type == StNodeType.LABEL) {
|
||||||
|
require(call.void)
|
||||||
|
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||||
|
|
||||||
if(sub is PtAsmSub) {
|
if(sub is PtAsmSub) {
|
||||||
@ -40,27 +54,109 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
||||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" jsr $subAsmName")
|
val bank = sub.address?.constbank?.toString()
|
||||||
|
if(bank==null) {
|
||||||
|
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||||
|
if(varbank!=null) {
|
||||||
|
if(asmgen.options.romable)
|
||||||
|
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
|
||||||
|
|
||||||
|
// self-modifying code: set jsrfar bank argument
|
||||||
|
when(asmgen.options.compTarget.name) {
|
||||||
|
"cx16" -> {
|
||||||
|
// JSRFAR can jump to a banked RAM address as well!
|
||||||
|
asmgen.out("""
|
||||||
|
php
|
||||||
|
pha
|
||||||
|
lda $varbank
|
||||||
|
sta +
|
||||||
|
pla
|
||||||
|
plp
|
||||||
|
jsr cx16.JSRFAR
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
+ .byte 0 ; modified"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"c64" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
php
|
||||||
|
pha
|
||||||
|
lda $varbank
|
||||||
|
sta +
|
||||||
|
pla
|
||||||
|
plp
|
||||||
|
jsr c64.x16jsrfar
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
+ .byte 0 ; modified"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"c128" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
php
|
||||||
|
pha
|
||||||
|
lda $varbank
|
||||||
|
sta +
|
||||||
|
pla
|
||||||
|
plp
|
||||||
|
jsr c128.x16jsrfar
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
+ .byte 0 ; modified"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asmgen.out(" jsr $subAsmName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
when(asmgen.options.compTarget.name) {
|
||||||
|
"cx16" -> {
|
||||||
|
// JSRFAR can jump to a banked RAM address as well!
|
||||||
|
asmgen.out("""
|
||||||
|
jsr cx16.JSRFAR
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
.byte $bank"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"c64" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
jsr c64.x16jsrfar
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
.byte $bank"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"c128" -> {
|
||||||
|
asmgen.out("""
|
||||||
|
jsr c128.x16jsrfar
|
||||||
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
|
.byte $bank"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(sub is PtSub) {
|
else if(sub is PtSub) {
|
||||||
if(optimizeIntArgsViaRegisters(sub)) {
|
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||||
if(sub.parameters.size==1) {
|
// Note that if the args fit into cpu registers, we don't concern ourselves here
|
||||||
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
// That is now up to the subroutine itself.
|
||||||
} else {
|
useCpuRegistersForArgs(call.args, sub)
|
||||||
// 2 byte params, second in Y, first in A
|
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
|
||||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
|
||||||
asmgen.out(" pha")
|
|
||||||
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
|
||||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
|
||||||
asmgen.out(" pla")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// arguments via variables
|
// arguments via variables
|
||||||
for(arg in sub.parameters.withIndex().zip(call.args))
|
val paramValues = sub.parameters.zip(call.args)
|
||||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||||
|
if (normalParams.isNotEmpty()) {
|
||||||
|
for (p in normalParams)
|
||||||
|
argumentViaVariable(sub, p.first, p.second)
|
||||||
|
}
|
||||||
|
if (registerParams.isNotEmpty()) {
|
||||||
|
// the R0-R15 'registers' are not really registers. They're just special variables.
|
||||||
|
for (p in registerParams)
|
||||||
|
argumentViaVariable(sub, p.first, p.second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subAsmName")
|
asmgen.out(" jsr $subAsmName")
|
||||||
}
|
}
|
||||||
@ -69,11 +165,45 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||||
|
val params = sub.parameters
|
||||||
|
when(params.size) {
|
||||||
|
1 -> {
|
||||||
|
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], register)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
|
||||||
|
// 2 byte params, second in Y, first in A
|
||||||
|
if(asmgen.needAsaveForExpr(args[0]) && !asmgen.needAsaveForExpr(args[1])) {
|
||||||
|
// first 0 then 1
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
|
} else if(!asmgen.needAsaveForExpr(args[0]) && asmgen.needAsaveForExpr(args[1])) {
|
||||||
|
// first 1 then 0
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
|
} else {
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||||
|
if (asmgen.needAsaveForExpr(args[1]))
|
||||||
|
asmgen.out(" pha")
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||||
|
if (asmgen.needAsaveForExpr(args[1]))
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("cannot use registers for word+byte")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("cannot use cpu registers for >2 arguments")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
|
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
|
||||||
return when(arg) {
|
return when(arg) {
|
||||||
is PtBuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
if (arg.name == "lsb" || arg.name == "msb")
|
if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
|
||||||
return usesOtherRegistersWhileEvaluating(arg.args[0])
|
return usesOtherRegistersWhileEvaluating(arg.args[0])
|
||||||
if (arg.name == "mkword")
|
if (arg.name == "mkword")
|
||||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
|
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
|
||||||
@ -104,9 +234,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
val param = sub.parameters[it]
|
val param = sub.parameters[it]
|
||||||
val arg = call.args[it]
|
val arg = call.args[it]
|
||||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
|
||||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||||
else if(registersUsed.any {it.statusflag!=null}) {
|
else if(registersUsed.any { r-> r.statusflag!=null }) {
|
||||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -131,8 +261,15 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
if(!isArgumentTypeCompatible(value.type, parameter.type))
|
if(!isArgumentTypeCompatible(value.type, parameter.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
val reg = parameter.register
|
||||||
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
if(reg!=null) {
|
||||||
|
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||||
|
val varName = "cx16.${reg.name.lowercase()}"
|
||||||
|
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
||||||
|
} else {
|
||||||
|
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
||||||
|
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
|
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
|
||||||
@ -148,7 +285,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
val requiredDt = parameter.value.type
|
val requiredDt = parameter.value.type
|
||||||
if(requiredDt!=value.type) {
|
if(requiredDt!=value.type) {
|
||||||
if(value.type largerThan requiredDt)
|
if(value.type.largerSizeThan(requiredDt))
|
||||||
throw AssemblyError("can only convert byte values to word param types")
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
}
|
}
|
||||||
if (statusflag!=null) {
|
if (statusflag!=null) {
|
||||||
@ -187,21 +324,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
else {
|
else {
|
||||||
// via register or register pair
|
// via register or register pair
|
||||||
register!!
|
register!!
|
||||||
if(requiredDt largerThan value.type) {
|
if(requiredDt.largerSizeThan(value.type)) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||||
} else {
|
} else {
|
||||||
val scope = value.definingISub()
|
val scope = value.definingISub()
|
||||||
val target: AsmAssignTarget =
|
val target: AsmAssignTarget =
|
||||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||||
else {
|
else {
|
||||||
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
|
||||||
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
|
|
||||||
}
|
}
|
||||||
val src = if(value.type in PassByReferenceDatatypes) {
|
val src = if(value.type.isPassByRef) {
|
||||||
if(value is PtIdentifier) {
|
if(value is PtIdentifier) {
|
||||||
val addr = PtAddressOf(Position.DUMMY)
|
val addr = PtAddressOf(Position.DUMMY)
|
||||||
addr.add(value)
|
addr.add(value)
|
||||||
@ -213,7 +349,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope)
|
asmgen.translateNormalAssignment(AsmAssignment(src, listOf(target), program.memsizer, target.position), scope)
|
||||||
}
|
}
|
||||||
return RegisterOrStatusflag(register, null)
|
return RegisterOrStatusflag(register, null)
|
||||||
}
|
}
|
||||||
@ -222,18 +358,18 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
if(argType isAssignableTo paramType)
|
if(argType isAssignableTo paramType)
|
||||||
return true
|
return true
|
||||||
if(argType==DataType.BOOL && paramType==DataType.BOOL)
|
if(argType.isBool && paramType.isBool)
|
||||||
return true
|
return true
|
||||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
if(argType.isByte && paramType.isByte)
|
||||||
return true
|
return true
|
||||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
if(argType.isWord && paramType.isWord)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
// we have a special rule for some types.
|
// we have a special rule for some types.
|
||||||
// strings are assignable to UWORD, for example, and vice versa
|
// strings are assignable to UWORD, for example, and vice versa
|
||||||
if(argType==DataType.STR && paramType==DataType.UWORD)
|
if(argType.isString && paramType.isUnsignedWord)
|
||||||
return true
|
return true
|
||||||
if(argType==DataType.UWORD && paramType == DataType.STR)
|
if(argType.isUnsignedWord && paramType.isString)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
File diff suppressed because it is too large
Load Diff
362
codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt
Normal file
362
codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
|
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||||
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
|
|
||||||
|
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
||||||
|
|
||||||
|
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
||||||
|
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
|
||||||
|
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
||||||
|
val endLabel = asmgen.makeLabel("ifexpr_end")
|
||||||
|
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||||
|
when {
|
||||||
|
expr.type.isByteOrBool -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||||
|
asmgen.jmp(endLabel)
|
||||||
|
asmgen.out(falseLabel)
|
||||||
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||||
|
}
|
||||||
|
expr.type.isWord || expr.type.isString -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||||
|
asmgen.jmp(endLabel)
|
||||||
|
asmgen.out(falseLabel)
|
||||||
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
expr.type.isFloat -> {
|
||||||
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.jmp(endLabel)
|
||||||
|
asmgen.out(falseLabel)
|
||||||
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||||
|
when (condition) {
|
||||||
|
is PtBinaryExpression -> {
|
||||||
|
val rightDt = condition.right.type
|
||||||
|
return when {
|
||||||
|
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||||
|
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||||
|
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is PtPrefix if condition.operator=="not" -> {
|
||||||
|
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
|
||||||
|
val signed = condition.left.type.isSigned
|
||||||
|
val constValue = condition.right.asConstInteger()
|
||||||
|
if(constValue==0) {
|
||||||
|
return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
when(condition.operator) {
|
||||||
|
"==" -> {
|
||||||
|
// if X==value
|
||||||
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||||
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
|
asmgen.out(" bne $falseLabel")
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
// if X!=value
|
||||||
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||||
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
in LogicalOperators -> {
|
||||||
|
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||||
|
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
} else {
|
||||||
|
errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-)
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
|
||||||
|
// TODO: special cases for <, <=, >, >= above.
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
|
||||||
|
// TODO can we reuse this whole thing from IfElse ?
|
||||||
|
val constValue = condition.right.asConstInteger()
|
||||||
|
if(constValue!=null) {
|
||||||
|
if (constValue == 0) {
|
||||||
|
when (condition.operator) {
|
||||||
|
"==" -> return translateWordExprIsZero(condition.left, falseLabel)
|
||||||
|
"!=" -> return translateWordExprIsNotZero(condition.left, falseLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (constValue != 0) {
|
||||||
|
when (condition.operator) {
|
||||||
|
"==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel)
|
||||||
|
"!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val variable = condition.right as? PtIdentifier
|
||||||
|
if(variable!=null) {
|
||||||
|
when (condition.operator) {
|
||||||
|
"==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel)
|
||||||
|
"!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
|
||||||
|
val constValue = (condition.right as? PtNumber)?.number
|
||||||
|
if(constValue==0.0) {
|
||||||
|
if (condition.operator == "==") {
|
||||||
|
// if FL==0.0
|
||||||
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel")
|
||||||
|
return
|
||||||
|
} else if(condition.operator=="!=") {
|
||||||
|
// if FL!=0.0
|
||||||
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
|
||||||
|
asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(condition.operator) {
|
||||||
|
"==" -> {
|
||||||
|
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
|
||||||
|
asmgen.out(" beq $elseLabel")
|
||||||
|
}
|
||||||
|
"!=" -> {
|
||||||
|
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
|
||||||
|
asmgen.out(" bne $elseLabel")
|
||||||
|
}
|
||||||
|
"<" -> {
|
||||||
|
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
|
||||||
|
asmgen.out(" beq $elseLabel")
|
||||||
|
}
|
||||||
|
"<=" -> {
|
||||||
|
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
|
||||||
|
asmgen.out(" beq $elseLabel")
|
||||||
|
}
|
||||||
|
">" -> {
|
||||||
|
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
|
||||||
|
asmgen.out(" bne $elseLabel")
|
||||||
|
}
|
||||||
|
">=" -> {
|
||||||
|
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
|
||||||
|
asmgen.out(" bne $elseLabel")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("expected comparison operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||||
|
// if w!=variable
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
val varRight = asmgen.asmVariableName(variable)
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varLeft = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varLeft
|
||||||
|
cmp $varRight
|
||||||
|
bne +
|
||||||
|
lda $varLeft+1
|
||||||
|
cmp $varRight+1
|
||||||
|
beq $falseLabel
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varRight
|
||||||
|
bne +
|
||||||
|
cpy $varRight+1
|
||||||
|
beq $falseLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||||
|
// if w==variable
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
val varRight = asmgen.asmVariableName(variable)
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varLeft = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varLeft
|
||||||
|
cmp $varRight
|
||||||
|
bne $falseLabel
|
||||||
|
lda $varLeft+1
|
||||||
|
cmp $varRight+1
|
||||||
|
bne $falseLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varRight
|
||||||
|
bne $falseLabel
|
||||||
|
cpy $varRight+1
|
||||||
|
bne $falseLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
|
||||||
|
// if w!=number
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varname = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #<$number
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$number
|
||||||
|
beq $falseLabel
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp #<$number
|
||||||
|
bne +
|
||||||
|
cpy #>$number
|
||||||
|
beq $falseLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
|
||||||
|
// if w==number
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varname = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #<$number
|
||||||
|
bne $falseLabel
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$number
|
||||||
|
bne $falseLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out( """
|
||||||
|
cmp #<$number
|
||||||
|
bne $falseLabel
|
||||||
|
cpy #>$number
|
||||||
|
bne $falseLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
|
||||||
|
// if w!=0
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varname = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
ora $varname+1
|
||||||
|
beq $falseLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) {
|
||||||
|
// if w==0
|
||||||
|
// TODO reuse code from ifElse?
|
||||||
|
if(expr is PtIdentifier) {
|
||||||
|
val varname = asmgen.asmVariableName(expr)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
ora $varname+1
|
||||||
|
bne $falseLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
||||||
|
// optimized code for byte comparisons with 0
|
||||||
|
|
||||||
|
val useBIT = asmgen.checkIfConditionCanUseBIT(condition)
|
||||||
|
if(useBIT!=null) {
|
||||||
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
|
val (testForBitSet, variable, bitmask) = useBIT
|
||||||
|
when (bitmask) {
|
||||||
|
128 -> {
|
||||||
|
// test via bit + N flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) asmgen.out(" bpl $falseLabel")
|
||||||
|
else asmgen.out(" bmi $falseLabel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
64 -> {
|
||||||
|
// test via bit + V flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) asmgen.out(" bvc $falseLabel")
|
||||||
|
else asmgen.out(" bvs $falseLabel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("BIT can only work on bits 7 and 6")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition.left)
|
||||||
|
when (condition.operator) {
|
||||||
|
"==" -> asmgen.out(" bne $falseLabel")
|
||||||
|
"!=" -> asmgen.out(" beq $falseLabel")
|
||||||
|
">" -> {
|
||||||
|
if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel")
|
||||||
|
else asmgen.out(" beq $falseLabel")
|
||||||
|
}
|
||||||
|
">=" -> {
|
||||||
|
if(signed) asmgen.out(" bmi $falseLabel")
|
||||||
|
else { /* always true for unsigned */ }
|
||||||
|
}
|
||||||
|
"<" -> {
|
||||||
|
if(signed) asmgen.out(" bpl $falseLabel")
|
||||||
|
else asmgen.jmp(falseLabel)
|
||||||
|
}
|
||||||
|
"<=" -> {
|
||||||
|
if(signed) {
|
||||||
|
// inverted '>'
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
bpl $falseLabel
|
||||||
|
+""")
|
||||||
|
} else asmgen.out(" bne $falseLabel")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("expected comparison operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,10 +32,6 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
internal fun generate() {
|
internal fun generate() {
|
||||||
header()
|
header()
|
||||||
val allBlocks = program.allBlocks()
|
|
||||||
|
|
||||||
if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main")
|
|
||||||
throw AssemblyError("first block should be 'main' or 'p8b_main'")
|
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
program.allBlocks().forEach { block2asm(it) }
|
program.allBlocks().forEach { block2asm(it) }
|
||||||
@ -43,22 +39,21 @@ internal class ProgramAndVarsGen(
|
|||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
asmgen.out("; global float constants")
|
asmgen.out("; global float constants")
|
||||||
for (flt in allocator.globalFloatConsts) {
|
for (flt in allocator.globalFloatConsts) {
|
||||||
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key)
|
val floatFill = compTarget.getFloatAsmBytes(flt.key)
|
||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
|
|
||||||
memorySlabs()
|
memorySlabs()
|
||||||
tempVars()
|
|
||||||
footer()
|
footer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun header() {
|
private fun header() {
|
||||||
val ourName = this.javaClass.name
|
val ourName = this.javaClass.name
|
||||||
val cpu = when(compTarget.machine.cpu) {
|
val cpu = when(compTarget.cpu) {
|
||||||
CpuType.CPU6502 -> "6502"
|
CpuType.CPU6502 -> "6502"
|
||||||
CpuType.CPU65c02 -> "w65c02"
|
CpuType.CPU65C02 -> "w65c02"
|
||||||
else -> "unsupported"
|
else -> "unsupported"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +70,12 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||||
|
if(compTarget.name=="c64") {
|
||||||
|
if(options.floats)
|
||||||
|
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
|
||||||
|
else
|
||||||
|
asmgen.out("PROG8_C64_BANK_CONFIG=30 ; IO+kernal, no basic")
|
||||||
|
}
|
||||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||||
asmgen.out(".endweak")
|
asmgen.out(".endweak")
|
||||||
|
|
||||||
@ -85,71 +86,122 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when(options.output) {
|
if(options.compTarget.customLauncher.isNotEmpty()) {
|
||||||
OutputType.RAW -> {
|
asmgen.out("; ---- custom launcher assembler program ----")
|
||||||
asmgen.out("; ---- raw assembler program ----")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
for(line in options.compTarget.customLauncher) {
|
||||||
|
asmgen.out(line)
|
||||||
}
|
}
|
||||||
OutputType.PRG -> {
|
return
|
||||||
when(options.launcher) {
|
}
|
||||||
CbmPrgLauncherType.BASIC -> {
|
|
||||||
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
if(options.output == OutputType.LIBRARY) {
|
||||||
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
|
|
||||||
|
asmgen.out("; ---- library assembler program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" jmp p8b_main.p8s_start")
|
||||||
|
// note: the jmp above has 2 effects:
|
||||||
|
// 1. it prevents 64tass from stripping away all procs as unused code
|
||||||
|
// 2. it functions as the first entrypoint of the library, required anyway, to run the variable initialization/bss clear bootstrap code.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
when (options.output) {
|
||||||
|
OutputType.LIBRARY -> { }
|
||||||
|
OutputType.RAW -> {
|
||||||
|
asmgen.out("; ---- raw assembler program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
|
OutputType.PRG -> {
|
||||||
|
when (options.launcher) {
|
||||||
|
CbmPrgLauncherType.BASIC -> {
|
||||||
|
if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
|
||||||
|
errors.err(
|
||||||
|
"BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}",
|
||||||
|
program.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
asmgen.out("; ---- basic program with sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
val year = LocalDate.now().year
|
||||||
|
asmgen.out(" .word (+), $year")
|
||||||
|
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||||
|
asmgen.out("+\t.word 0")
|
||||||
|
asmgen.out("prog8_entrypoint")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
|
|
||||||
|
CbmPrgLauncherType.NONE -> {
|
||||||
|
// this is the same as RAW
|
||||||
|
asmgen.out("; ---- program without basic sys call ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
}
|
}
|
||||||
asmgen.out("; ---- basic program with sys call ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
val year = LocalDate.now().year
|
|
||||||
asmgen.out(" .word (+), $year")
|
|
||||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
|
||||||
asmgen.out("+\t.word 0")
|
|
||||||
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr sys.init_system")
|
|
||||||
asmgen.out(" jsr sys.init_system_phase2")
|
|
||||||
}
|
|
||||||
CbmPrgLauncherType.NONE -> {
|
|
||||||
asmgen.out("; ---- program without basic sys call ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr sys.init_system")
|
|
||||||
asmgen.out(" jsr sys.init_system_phase2")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OutputType.XEX -> {
|
||||||
|
asmgen.out("; ---- atari xex program ----")
|
||||||
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
|
asmgen.out("prog8_program_start\t; start of program label")
|
||||||
|
asmgen.out(" cld")
|
||||||
|
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||||
|
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||||
|
if (!options.noSysInit)
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||||
|
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutputType.XEX -> {
|
|
||||||
asmgen.out("; ---- atari xex program ----")
|
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
|
||||||
if(!options.noSysInit)
|
|
||||||
asmgen.out(" jsr sys.init_system")
|
|
||||||
asmgen.out(" jsr sys.init_system_phase2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
if (options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
; zeropage is clobbered so we need to reset the machine at exit
|
; zeropage is clobbered so we need to reset the machine at exit
|
||||||
lda #>sys.reset_system
|
lda #>sys.reset_system
|
||||||
pha
|
pha
|
||||||
lda #<sys.reset_system
|
lda #<sys.reset_system
|
||||||
pha""")
|
pha""")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(compTarget.name) {
|
when (compTarget.name) {
|
||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
if(options.floats)
|
if (options.floats)
|
||||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||||
asmgen.out(" jsr p8b_main.p8s_start")
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
|
}
|
||||||
|
"c64" -> {
|
||||||
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
|
}
|
||||||
|
"c128" -> {
|
||||||
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.out(" jsr p8b_main.p8s_start")
|
||||||
|
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"c64" -> {
|
|
||||||
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
|
|
||||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
|
||||||
}
|
|
||||||
"c128" -> {
|
|
||||||
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
|
|
||||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
|
||||||
}
|
|
||||||
else -> asmgen.jmp("p8b_main.p8s_start")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,27 +218,6 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tempVars() {
|
|
||||||
asmgen.out("; expression temp vars\n .section BSS")
|
|
||||||
for((dt, count) in asmgen.tempVarsCounters) {
|
|
||||||
if(count>0) {
|
|
||||||
for(num in 1..count) {
|
|
||||||
val name = asmgen.buildTempVarName(dt, num)
|
|
||||||
when (dt) {
|
|
||||||
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() {
|
private fun footer() {
|
||||||
var relocateBssVars = false
|
var relocateBssVars = false
|
||||||
var relocateBssSlabs = false
|
var relocateBssSlabs = false
|
||||||
@ -194,49 +225,49 @@ internal class ProgramAndVarsGen(
|
|||||||
var relocatedBssEnd = 0u
|
var relocatedBssEnd = 0u
|
||||||
|
|
||||||
if(options.varsGolden) {
|
if(options.varsGolden) {
|
||||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
relocateBssVars = true
|
relocateBssVars = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
|
||||||
}
|
}
|
||||||
else if(options.varsHighBank!=null) {
|
else if(options.varsHighBank!=null) {
|
||||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||||
throw AssemblyError("slabs and vars high bank must be the same")
|
throw AssemblyError("slabs and vars high bank must be the same")
|
||||||
relocateBssVars = true
|
relocateBssVars = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.slabsGolden) {
|
if(options.slabsGolden) {
|
||||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
relocateBssSlabs = true
|
relocateBssSlabs = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
|
||||||
}
|
}
|
||||||
else if(options.slabsHighBank!=null) {
|
else if(options.slabsHighBank!=null) {
|
||||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||||
}
|
}
|
||||||
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||||
throw AssemblyError("slabs and vars high bank must be the same")
|
throw AssemblyError("slabs and vars high bank must be the same")
|
||||||
relocateBssSlabs = true
|
relocateBssSlabs = true
|
||||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out("; bss sections")
|
asmgen.out("; bss sections")
|
||||||
@ -246,6 +277,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .dsection slabs_BSS")
|
asmgen.out(" .dsection slabs_BSS")
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
if(relocateBssSlabs)
|
if(relocateBssSlabs)
|
||||||
@ -253,6 +285,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
} else {
|
} else {
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
@ -265,6 +298,12 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(relocatedBssEnd >= options.memtopAddress)
|
||||||
|
options.memtopAddress = relocatedBssEnd+1u
|
||||||
|
|
||||||
|
asmgen.out(" ; memtop check")
|
||||||
|
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun block2asm(block: PtBlock) {
|
private fun block2asm(block: PtBlock) {
|
||||||
@ -272,12 +311,6 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("; ---- block: '${block.name}' ----")
|
asmgen.out("; ---- block: '${block.name}' ----")
|
||||||
if(block.options.address!=null)
|
if(block.options.address!=null)
|
||||||
asmgen.out("* = ${block.options.address!!.toHex()}")
|
asmgen.out("* = ${block.options.address!!.toHex()}")
|
||||||
else {
|
|
||||||
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
|
|
||||||
asmgen.out("\t.align 2")
|
|
||||||
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
|
|
||||||
asmgen.out("\t.align $100")
|
|
||||||
}
|
|
||||||
|
|
||||||
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
|
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
|
||||||
asmgen.outputSourceLine(block)
|
asmgen.outputSourceLine(block)
|
||||||
@ -297,7 +330,9 @@ internal class ProgramAndVarsGen(
|
|||||||
initializers.forEach { assign ->
|
initializers.forEach { assign ->
|
||||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||||
asmgen.translate(assign)
|
asmgen.translate(assign)
|
||||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
else
|
||||||
|
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
|
||||||
|
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||||
}
|
}
|
||||||
asmgen.out(" rts\n .bend")
|
asmgen.out(" rts\n .bend")
|
||||||
}
|
}
|
||||||
@ -400,22 +435,35 @@ internal class ProgramAndVarsGen(
|
|||||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||||
entrypointInitialization()
|
entrypointInitialization()
|
||||||
|
|
||||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
val params = sub.parameters
|
||||||
asmgen.out("; simple int arg(s) passed via register(s)")
|
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||||
if(sub.parameters.size==1) {
|
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||||
val dt = sub.parameters[0].type
|
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
fun varname(param: PtSubroutineParameter): String =
|
||||||
if(dt in ByteDatatypesWithBoolean)
|
if(param.register==null)
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
param.name
|
||||||
else
|
else
|
||||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
param.register!!.asScopedNameVirtualReg(param.type).joinToString(".")
|
||||||
} else {
|
|
||||||
require(sub.parameters.size==2)
|
when(params.size) {
|
||||||
// 2 simple byte args, first in A, second in Y
|
1 -> {
|
||||||
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
val dt = params[0].type
|
||||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, params[0].position, variableAsmName = varname(params[0]))
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target1)
|
if(dt.isByteOrBool)
|
||||||
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
asmgen.assignRegister(RegisterOrPair.A, target) // single byte in A
|
||||||
|
else
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, target) // word in AY
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[0].type, sub, params[0].position, variableAsmName = varname(params[0]))
|
||||||
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[1].type, sub, params[1].position, variableAsmName = varname(params[1]))
|
||||||
|
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
|
||||||
|
// 2 byte args, first in A, second in Y
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||||
|
} else throw AssemblyError("cannot use registers for word+byte args")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("cannot use registers for >2 args")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,23 +471,23 @@ internal class ProgramAndVarsGen(
|
|||||||
sub.children.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
asmgen.out("; variables")
|
asmgen.out("; variables")
|
||||||
asmgen.out(" .section BSS")
|
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||||
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
asmgen.out(" .send BSS")
|
asmgen.out(" .send BSS_NOCLEAR")
|
||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
@ -451,22 +499,11 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun entrypointInitialization() {
|
private fun entrypointInitialization() {
|
||||||
asmgen.out("; program startup initialization")
|
// zero out the BSS area first, before setting the variable init values
|
||||||
asmgen.out(" cld | tsx | stx prog8_lib.orig_stackpointer ; required for sys.exit()")
|
// this is mainly to make sure the arrays are all zero'd out at program startup
|
||||||
// set full BSS area to zero
|
asmgen.out(" jsr prog8_lib.program_startup_clear_bss")
|
||||||
asmgen.out("""
|
|
||||||
.if prog8_bss_section_size>0
|
|
||||||
; reset all variables in BSS section to zero
|
|
||||||
lda #<prog8_bss_section_start
|
|
||||||
ldy #>prog8_bss_section_start
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
ldx #<prog8_bss_section_size
|
|
||||||
ldy #>prog8_bss_section_size
|
|
||||||
lda #0
|
|
||||||
jsr prog8_lib.memset
|
|
||||||
.endif""")
|
|
||||||
|
|
||||||
|
// initialize block-level (global) variables at program start
|
||||||
blockVariableInitializers.forEach {
|
blockVariableInitializers.forEach {
|
||||||
if (it.value.isNotEmpty())
|
if (it.value.isNotEmpty())
|
||||||
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
||||||
@ -509,17 +546,15 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
stringVarsWithInitInZp.forEach {
|
stringVarsWithInitInZp.forEach {
|
||||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||||
outputStringvar(varname, it.value.second, it.value.first)
|
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayVarsWithInitInZp.forEach {
|
arrayVarsWithInitInZp.forEach {
|
||||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||||
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
|
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out("""+
|
asmgen.out("+")
|
||||||
clv
|
|
||||||
clc""")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ZpStringWithInitial(
|
private class ZpStringWithInitial(
|
||||||
@ -536,24 +571,24 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
|
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
|
||||||
val result = mutableListOf<ZpStringWithInitial>()
|
val result = mutableListOf<ZpStringWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
val vars = allocator.zeropageVars.filter { it.value.dt.isString }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key
|
val scopedName = variable.key
|
||||||
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||||
if(svar?.onetimeInitializationStringValue!=null)
|
if(svar?.initializationStringValue!=null)
|
||||||
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
result.add(ZpStringWithInitial(scopedName, variable.value, svar.initializationStringValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
|
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
|
||||||
val result = mutableListOf<ZpArrayWithInitial>()
|
val result = mutableListOf<ZpArrayWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
val vars = allocator.zeropageVars.filter { it.value.dt.isArray }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key
|
val scopedName = variable.key
|
||||||
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||||
if(svar?.onetimeInitializationArrayValue!=null)
|
if(svar?.initializationArrayValue!=null)
|
||||||
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.initializationArrayValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -564,7 +599,7 @@ internal class ProgramAndVarsGen(
|
|||||||
if (scopedName.startsWith("cx16.r"))
|
if (scopedName.startsWith("cx16.r"))
|
||||||
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
||||||
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
|
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||||
if(variable.dt in SplitWordArrayTypes) {
|
if(variable.dt.isSplitWordArray) {
|
||||||
val lsbAddr = zpvar.address
|
val lsbAddr = zpvar.address
|
||||||
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
|
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
|
||||||
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
|
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
|
||||||
@ -579,43 +614,90 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||||
if(varsNoInit.isNotEmpty()) {
|
if(varsNoInit.isNotEmpty()) {
|
||||||
asmgen.out("; non-zeropage variables without initialization value")
|
asmgen.out("; non-zeropage variables")
|
||||||
asmgen.out(" .section BSS")
|
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||||
varsNoInit.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
|
|
||||||
uninitializedVariable2asm(it)
|
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||||
|
asmgen.out(" .section $section")
|
||||||
|
val (notAligned, aligned) = variables.partition { it.align == 0 }
|
||||||
|
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||||
|
uninitializedVariable2asm(it)
|
||||||
|
}
|
||||||
|
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
|
||||||
|
.forEach { uninitializedVariable2asm(it) }
|
||||||
|
asmgen.out(" .send $section")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(clean.isNotEmpty()) {
|
||||||
|
// clean vars end up in BSS so they're at least cleared to 0 at startup
|
||||||
|
generate("BSS", clean)
|
||||||
|
}
|
||||||
|
if(dirty.isNotEmpty()) {
|
||||||
|
// Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
|
||||||
|
// but NOT at each entry of the subroutine they're declared in.
|
||||||
|
// This saves the STZ's instructions in the subroutine, while still having deterministic start state.
|
||||||
|
// So there is no actual difference here when compared to the way non-dirty variables are allocated.
|
||||||
|
generate("BSS", dirty)
|
||||||
}
|
}
|
||||||
asmgen.out(" .send BSS")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(varsWithInit.isNotEmpty()) {
|
if(varsWithInit.isNotEmpty()) {
|
||||||
asmgen.out("; non-zeropage variables")
|
asmgen.out("; non-zeropage variables with init value")
|
||||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
|
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
|
||||||
stringvars.forEach {
|
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
|
||||||
|
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||||
|
notAlignedStrings.forEach {
|
||||||
outputStringvar(
|
outputStringvar(
|
||||||
it.name,
|
it.name,
|
||||||
it.onetimeInitializationStringValue!!.second,
|
it.align,
|
||||||
it.onetimeInitializationStringValue!!.first
|
it.initializationStringValue!!.second,
|
||||||
|
it.initializationStringValue!!.first
|
||||||
)
|
)
|
||||||
|
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
othervars.sortedBy { it.type }.forEach {
|
alignedStrings.sortedBy { it.align }.forEach {
|
||||||
|
outputStringvar(
|
||||||
|
it.name,
|
||||||
|
it.align,
|
||||||
|
it.initializationStringValue!!.second,
|
||||||
|
it.initializationStringValue!!.first
|
||||||
|
)
|
||||||
|
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
|
}
|
||||||
|
|
||||||
|
notAlignedOther.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
|
if(it.dt.isArray)
|
||||||
|
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
|
else
|
||||||
|
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||||
|
}
|
||||||
|
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||||
|
staticVariable2asm(it)
|
||||||
|
if(it.dt.isArray)
|
||||||
|
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
|
else
|
||||||
|
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
||||||
when (variable.dt) {
|
val dt = variable.dt
|
||||||
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
when {
|
||||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
|
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
|
||||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
||||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
||||||
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
||||||
in SplitWordArrayTypes -> {
|
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||||
|
dt.isSplitWordArray -> {
|
||||||
|
alignVar(variable.align)
|
||||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||||
}
|
}
|
||||||
in ArrayDatatypes -> {
|
dt.isArray -> {
|
||||||
|
alignVar(variable.align)
|
||||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||||
}
|
}
|
||||||
@ -625,41 +707,56 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
private fun alignVar(align: Int) {
|
||||||
val initialValue: Number =
|
if(align > 1)
|
||||||
if(variable.onetimeInitializationNumericValue!=null) {
|
asmgen.out(" .align ${align.toHex()}")
|
||||||
if(variable.dt== DataType.FLOAT)
|
}
|
||||||
variable.onetimeInitializationNumericValue!!
|
|
||||||
else
|
|
||||||
variable.onetimeInitializationNumericValue!!.toInt()
|
|
||||||
} else 0
|
|
||||||
|
|
||||||
when (variable.dt) {
|
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||||
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
if(!variable.dt.isArray && !variable.dt.isString) {
|
||||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
|
throw AssemblyError("static variables with an initialization value can only be an array or a string, not ${variable.dt} (${variable.name} ${variable.astNode?.position}")
|
||||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
// because numeric variables are in the BSS section and get initialized via assignment statements
|
||||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
|
}
|
||||||
DataType.FLOAT -> {
|
|
||||||
if(initialValue==0) {
|
// val initialValue: Number =
|
||||||
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
// if(variable.initializationNumericValue!=null) {
|
||||||
} else {
|
// if(variable.dt.isFloat)
|
||||||
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
// variable.initializationNumericValue!!
|
||||||
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
// else
|
||||||
}
|
// variable.initializationNumericValue!!.toInt()
|
||||||
}
|
// } else 0
|
||||||
DataType.STR -> {
|
//
|
||||||
|
|
||||||
|
val dt=variable.dt
|
||||||
|
when {
|
||||||
|
// dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||||
|
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||||
|
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||||
|
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||||
|
// dt.isFloat -> {
|
||||||
|
// if(initialValue==0) {
|
||||||
|
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||||
|
// } else {
|
||||||
|
// val floatFill = compTarget.getFloatAsmBytes(initialValue)
|
||||||
|
// asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
dt.isString -> {
|
||||||
throw AssemblyError("all string vars should have been interned into prog")
|
throw AssemblyError("all string vars should have been interned into prog")
|
||||||
}
|
}
|
||||||
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
|
dt.isArray -> {
|
||||||
|
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw AssemblyError("weird dt")
|
throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
|
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||||
when(dt) {
|
alignVar(align)
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
|
when {
|
||||||
|
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
||||||
@ -669,7 +766,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .byte " + chunk.joinToString())
|
asmgen.out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
dt.isSignedByteArray -> {
|
||||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
asmgen.out("$varname\t.char ${data.joinToString()}")
|
asmgen.out("$varname\t.char ${data.joinToString()}")
|
||||||
@ -679,7 +776,20 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .char " + chunk.joinToString())
|
asmgen.out(" .char " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
dt.isSplitWordArray -> {
|
||||||
|
if(dt.elementType().isUnsignedWord) {
|
||||||
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
|
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||||
|
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||||
|
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||||
|
} else {
|
||||||
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
|
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||||
|
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||||
|
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dt.isUnsignedWordArray -> {
|
||||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
asmgen.out("$varname\t.word ${data.joinToString()}")
|
asmgen.out("$varname\t.word ${data.joinToString()}")
|
||||||
@ -689,7 +799,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .word " + chunk.joinToString())
|
asmgen.out(" .word " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
dt.isSignedWordArray -> {
|
||||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
asmgen.out("$varname\t.sint ${data.joinToString()}")
|
asmgen.out("$varname\t.sint ${data.joinToString()}")
|
||||||
@ -699,22 +809,10 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .sint " + chunk.joinToString())
|
asmgen.out(" .sint " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW_SPLIT -> {
|
dt.isFloatArray -> {
|
||||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
|
||||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
|
||||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
|
||||||
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_W_SPLIT -> {
|
|
||||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
|
||||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
|
||||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
|
||||||
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
compTarget.machine.getFloatAsmBytes(it.number!!)
|
compTarget.getFloatAsmBytes(it.number!!)
|
||||||
}
|
}
|
||||||
asmgen.out(varname)
|
asmgen.out(varname)
|
||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
@ -737,7 +835,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
||||||
}
|
}
|
||||||
consts.sortedBy { it.name }.forEach {
|
consts.sortedBy { it.name }.forEach {
|
||||||
if(it.dt==DataType.FLOAT)
|
if(it.dt==BaseDataType.FLOAT)
|
||||||
asmgen.out(" ${it.name} = ${it.value}")
|
asmgen.out(" ${it.name} = ${it.value}")
|
||||||
else
|
else
|
||||||
asmgen.out(" ${it.name} = ${it.value.toHex()}")
|
asmgen.out(" ${it.name} = ${it.value.toHex()}")
|
||||||
@ -749,11 +847,16 @@ internal class ProgramAndVarsGen(
|
|||||||
.filter { it is PtAsmSub && it.address!=null }
|
.filter { it is PtAsmSub && it.address!=null }
|
||||||
.forEach { asmsub ->
|
.forEach { asmsub ->
|
||||||
asmsub as PtAsmSub
|
asmsub as PtAsmSub
|
||||||
asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
|
val address = asmsub.address!!
|
||||||
|
val bank = if(address.constbank!=null) "; @bank ${address.constbank}"
|
||||||
|
else if(address.varbank!=null) "; @bank ${address.varbank?.name}"
|
||||||
|
else ""
|
||||||
|
asmgen.out(" ${asmsub.name} = ${address.address.toHex()} $bank")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||||
|
alignVar(align)
|
||||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||||
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
@ -763,8 +866,8 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
return when (dt) {
|
return when {
|
||||||
DataType.ARRAY_BOOL ->
|
dt.isBoolArray ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
array.map {
|
array.map {
|
||||||
if(it.boolean!=null)
|
if(it.boolean!=null)
|
||||||
@ -774,18 +877,23 @@ internal class ProgramAndVarsGen(
|
|||||||
if(number==0.0) "0" else "1"
|
if(number==0.0) "0" else "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB ->
|
dt.isUnsignedByteArray ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
array.map {
|
array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
"$"+number.toString(16).padStart(2, '0')
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||||
if(it.number!=null) {
|
if(it.number!=null) {
|
||||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
else if(it.addressOfSymbol!=null) {
|
else if(it.addressOfSymbol!=null) {
|
||||||
asmgen.asmSymbolName(it.addressOfSymbol!!)
|
val addrOfSymbol = it.addressOfSymbol!!
|
||||||
|
val symbol = symboltable.lookup(addrOfSymbol)!!
|
||||||
|
if(symbol is StStaticVariable && symbol.dt.isSplitWordArray)
|
||||||
|
asmgen.asmSymbolName(addrOfSymbol+"_lsb") // the _lsb split array comes first in memory
|
||||||
|
else
|
||||||
|
asmgen.asmSymbolName(addrOfSymbol)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw AssemblyError("weird array elt")
|
throw AssemblyError("weird array elt")
|
||||||
@ -796,14 +904,14 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
return when (dt) {
|
return when {
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
DataType.ARRAY_UB ->
|
dt.isUnsignedByteArray ->
|
||||||
array.map {
|
array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
"$"+number.toString(16).padStart(2, '0')
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B ->
|
dt.isSignedByteArray ->
|
||||||
array.map {
|
array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
||||||
@ -812,11 +920,11 @@ internal class ProgramAndVarsGen(
|
|||||||
else
|
else
|
||||||
"-$$hexnum"
|
"-$$hexnum"
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
"$" + number.toString(16).padStart(4, '0')
|
"$" + number.toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
|
dt.isArray && dt.elementType().isSignedWord -> array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
if(number>=0)
|
if(number>=0)
|
||||||
|
@ -2,10 +2,7 @@ package prog8.codegen.cpu6502
|
|||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import com.github.michaelbull.result.onSuccess
|
import com.github.michaelbull.result.onSuccess
|
||||||
import prog8.code.StNode
|
import prog8.code.*
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
@ -14,7 +11,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
private val errors: IErrorReporter
|
private val errors: IErrorReporter
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val zeropage = options.compTarget.machine.zeropage
|
private val zeropage = options.compTarget.zeropage
|
||||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
||||||
|
|
||||||
@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
zeropageVars = zeropage.allocatedVariables
|
zeropageVars = zeropage.allocatedVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
internal fun isZpVar(scopedName: String): Boolean {
|
||||||
|
if(scopedName in zeropageVars)
|
||||||
|
return true
|
||||||
|
|
||||||
|
val v = symboltable.lookup(scopedName)
|
||||||
|
return if(v is StMemVar) v.address <= 255u else false
|
||||||
|
}
|
||||||
|
|
||||||
internal fun getFloatAsmConst(number: Double): String {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
val asmName = globalFloatConsts[number]
|
val asmName = globalFloatConsts[number]
|
||||||
@ -49,8 +52,8 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||||
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
|
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
|
||||||
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||||
|
|
||||||
var numVariablesAllocatedInZP = 0
|
var numVariablesAllocatedInZP = 0
|
||||||
var numberOfNonIntegerVariables = 0
|
var numberOfNonIntegerVariables = 0
|
||||||
@ -60,7 +63,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
variable.scopedName,
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.astNode.position,
|
variable.astNode?.position ?: Position.DUMMY,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.fold(
|
result.fold(
|
||||||
@ -68,7 +71,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
numVariablesAllocatedInZP++
|
numVariablesAllocatedInZP++
|
||||||
},
|
},
|
||||||
failure = {
|
failure = {
|
||||||
errors.err(it.message!!, variable.astNode.position)
|
errors.err(it.message!!, variable.astNode?.position ?: Position.DUMMY)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -79,7 +82,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
variable.scopedName,
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.astNode.position,
|
variable.astNode?.position ?: Position.DUMMY,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
@ -89,9 +92,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
// try to allocate the "don't care" 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? ...?
|
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
|
||||||
for (variable in sortedList) {
|
for (variable in sortedList) {
|
||||||
if(variable.dt in IntegerDatatypesWithBoolean) {
|
if(variable.dt.isIntegerOrBool) {
|
||||||
if(zeropage.free.isEmpty()) {
|
if(zeropage.free.isEmpty()) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
@ -99,7 +102,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
variable.scopedName,
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.astNode.position,
|
variable.astNode?.position ?: Position.DUMMY,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
@ -124,6 +127,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
collect(st)
|
collect(st)
|
||||||
return vars.sortedBy { it.dt }
|
return vars.sortedBy { it.dt.base }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
|
|
||||||
import prog8.code.ast.PtBinaryExpression
|
import prog8.code.ast.PtBinaryExpression
|
||||||
import prog8.code.ast.PtExpression
|
import prog8.code.ast.PtExpression
|
||||||
import prog8.code.core.*
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.ComparisonOperators
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.RegisterOrPair
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
@ -17,28 +20,28 @@ internal class AnyExprAsmGen(
|
|||||||
private val asmgen: AsmGen6502Internal
|
private val asmgen: AsmGen6502Internal
|
||||||
) {
|
) {
|
||||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
when(expr.type) {
|
when {
|
||||||
in ByteDatatypesWithBoolean -> {
|
expr.type.isByteOrBool -> {
|
||||||
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
|
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
|
||||||
return assignByteBinExpr(expr, assign)
|
return assignByteBinExpr(expr, assign)
|
||||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
if (expr.left.type.isWord && expr.right.type.isWord) {
|
||||||
require(expr.operator in ComparisonOperators)
|
require(expr.operator in ComparisonOperators)
|
||||||
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
|
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
|
||||||
}
|
}
|
||||||
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
if (expr.left.type.isFloat && expr.right.type.isFloat) {
|
||||||
require(expr.operator in ComparisonOperators)
|
require(expr.operator in ComparisonOperators)
|
||||||
return assignFloatBinExpr(expr, assign)
|
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 -> {
|
expr.type.isWord -> {
|
||||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
require(expr.left.type.isWord && expr.right.type.isWord) {
|
||||||
"both operands must be words"
|
"both operands must be words"
|
||||||
}
|
}
|
||||||
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
|
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
expr.type.isFloat -> {
|
||||||
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
require(expr.left.type.isFloat && expr.right.type.isFloat) {
|
||||||
"both operands must be floats"
|
"both operands must be floats"
|
||||||
}
|
}
|
||||||
return assignFloatBinExpr(expr, assign)
|
return assignFloatBinExpr(expr, assign)
|
||||||
@ -50,7 +53,7 @@ internal class AnyExprAsmGen(
|
|||||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||||
@ -58,7 +61,7 @@ internal class AnyExprAsmGen(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||||
@ -73,7 +76,7 @@ internal class AnyExprAsmGen(
|
|||||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||||
"&" -> {
|
"&" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||||
@ -81,7 +84,7 @@ internal class AnyExprAsmGen(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"|" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||||
@ -89,7 +92,7 @@ internal class AnyExprAsmGen(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||||
@ -175,17 +178,14 @@ internal class AnyExprAsmGen(
|
|||||||
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
|
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
|
||||||
when(asmgen.options.compTarget.name) {
|
when(asmgen.options.compTarget.name) {
|
||||||
C64Target.NAME -> {
|
C64Target.NAME -> {
|
||||||
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
|
// C64 math library has a quirk: you have always make sure FAC2/ARG is loaded last (done using CONUPK)
|
||||||
// this requires some more forced copying around of float values in certain cases
|
// otherwise the result of certain floating point operations such as FDIVT will be wrong.
|
||||||
if (right.isSimple()) {
|
// see https://www.c64-wiki.com/wiki/CONUPK
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
// Unfortunately this means we have to push and pop an intermediary floating point value to and from memory.
|
||||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
|
||||||
} else {
|
asmgen.pushFAC1()
|
||||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||||
asmgen.pushFAC1()
|
asmgen.popFAC2()
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
|
||||||
asmgen.popFAC2()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Cx16Target.NAME -> {
|
Cx16Target.NAME -> {
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||||
@ -193,7 +193,7 @@ internal class AnyExprAsmGen(
|
|||||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||||
if (!right.isSimple()) asmgen.popFAC1()
|
if (!right.isSimple()) asmgen.popFAC1()
|
||||||
}
|
}
|
||||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
else -> TODO("don't know how to evaluate float expression for selected compilation target ${left.position}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,14 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
VARIABLE,
|
VARIABLE,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
MEMORY,
|
MEMORY,
|
||||||
REGISTER
|
REGISTER,
|
||||||
|
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class SourceStorageKind {
|
internal enum class SourceStorageKind {
|
||||||
@ -44,11 +44,20 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(register!=null && datatype !in NumericDatatypesWithBoolean)
|
if(register!=null && !datatype.isNumericOrBool)
|
||||||
throw AssemblyError("must be numeric type")
|
throw AssemblyError("must be numeric type")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||||
|
return targets.map {
|
||||||
|
if(it.void)
|
||||||
|
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||||
|
else
|
||||||
|
fromAstAssignment(it, definingSub, asmgen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||||
with(target) {
|
with(target) {
|
||||||
when {
|
when {
|
||||||
@ -77,12 +86,20 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
|
RegisterOrPair.Y -> {
|
||||||
|
val dt = if(signed) DataType.BYTE else DataType.UBYTE
|
||||||
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
|
}
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
RegisterOrPair.XY -> {
|
||||||
|
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||||
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
|
}
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
RegisterOrPair.FAC2 -> {
|
||||||
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||||
|
}
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
RegisterOrPair.R2,
|
RegisterOrPair.R2,
|
||||||
@ -98,7 +115,10 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
RegisterOrPair.R15 -> {
|
||||||
|
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||||
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,9 +139,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
left isSameAs memory!!
|
left isSameAs memory!!
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> false
|
||||||
false
|
TargetStorageKind.VOID -> false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +182,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
||||||
val varName=asmgen.asmVariableName(value)
|
val varName=asmgen.asmVariableName(value)
|
||||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||||
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
if(value.type.isUnsignedWord && varName.lowercase().startsWith("cx16.r")) {
|
||||||
val regStr = varName.lowercase().substring(5)
|
val regStr = varName.lowercase().substring(5)
|
||||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
|
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
|
||||||
@ -183,9 +202,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
is PtFunctionCall -> {
|
is PtFunctionCall -> {
|
||||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||||
val sub = symbol.astNode as IPtSubroutine
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
val returnType =
|
||||||
|
if(sub is PtSub && sub.returns.size>1)
|
||||||
|
DataType.UNDEFINED // TODO list of types instead?
|
||||||
|
else
|
||||||
|
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -202,9 +224,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
|
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
|
||||||
|
|
||||||
if(target.datatype!=datatype) {
|
if(target.datatype!=datatype) {
|
||||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
if(target.datatype.isByte && datatype.isByte) {
|
||||||
return withAdjustedDt(target.datatype)
|
return withAdjustedDt(target.datatype)
|
||||||
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
|
} else if(target.datatype.isWord && datatype.isWord) {
|
||||||
return withAdjustedDt(target.datatype)
|
return withAdjustedDt(target.datatype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,26 +237,30 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
|
|
||||||
|
|
||||||
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||||
val target: AsmAssignTarget,
|
val targets: List<AsmAssignTarget>,
|
||||||
val memsizer: IMemSizer,
|
val memsizer: IMemSizer,
|
||||||
val position: Position) {
|
val position: Position) {
|
||||||
init {
|
init {
|
||||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
targets.forEach { target ->
|
||||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
if (!source.datatype.isArray && !source.datatype.isUndefined && !target.datatype.isArray && !target.datatype.isUndefined)
|
||||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
||||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val target: AsmAssignTarget
|
||||||
|
get() = targets.single()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignment(source: AsmAssignSource,
|
internal class AsmAssignment(source: AsmAssignSource,
|
||||||
target: AsmAssignTarget,
|
targets: List<AsmAssignTarget>,
|
||||||
memsizer: IMemSizer,
|
memsizer: IMemSizer,
|
||||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
position: Position): AsmAssignmentBase(source, targets, memsizer, position)
|
||||||
|
|
||||||
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
||||||
val operator: String,
|
val operator: String,
|
||||||
target: AsmAssignTarget,
|
target: AsmAssignTarget,
|
||||||
memsizer: IMemSizer,
|
memsizer: IMemSizer,
|
||||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
position: Position): AsmAssignmentBase(source, listOf(target), memsizer, position)
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,16 +4,25 @@ import prog8.code.core.*
|
|||||||
|
|
||||||
|
|
||||||
internal object DummyMemsizer : IMemSizer {
|
internal object DummyMemsizer : IMemSizer {
|
||||||
override fun memorySize(dt: DataType) = when(dt) {
|
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||||
in ByteDatatypesWithBoolean -> 1
|
if(dt.isArray) {
|
||||||
DataType.FLOAT -> 5
|
require(numElements != null)
|
||||||
else -> 2
|
return when(dt.sub) {
|
||||||
|
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||||
|
BaseDataType.FLOAT -> numElements*5
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
|
dt.isFloat -> 5 * (numElements ?: 1)
|
||||||
|
else -> 2 * (numElements ?: 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
|
|
||||||
DataType.ARRAY_UW -> numElements*2
|
override fun memorySize(dt: BaseDataType): Int {
|
||||||
DataType.ARRAY_W -> numElements*2
|
return memorySize(DataType.forDt(dt), null)
|
||||||
DataType.ARRAY_F -> numElements*5
|
|
||||||
else -> numElements
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||||
|
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||||
|
@ -5,11 +5,15 @@ import io.kotest.assertions.withClue
|
|||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.StMemVar
|
||||||
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.source.SourceCode
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502
|
import prog8.codegen.cpu6502.AsmGen6502
|
||||||
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
@ -25,8 +29,10 @@ class TestCodegen: FunSpec({
|
|||||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||||
floats = true,
|
floats = true,
|
||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,26 +49,62 @@ class TestCodegen: FunSpec({
|
|||||||
// xx += cx16.r0
|
// xx += cx16.r0
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
val codegen = AsmGen6502(prefixSymbols = false)
|
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
sub.add(PtVariable(
|
||||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
"pi",
|
||||||
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
DataType.UBYTE,
|
||||||
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
|
ZeropageWish.DONTCARE,
|
||||||
|
0u,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Position.DUMMY
|
||||||
|
))
|
||||||
|
sub.add(PtVariable(
|
||||||
|
"particleX",
|
||||||
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
|
ZeropageWish.DONTCARE,
|
||||||
|
0u,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
3u,
|
||||||
|
Position.DUMMY
|
||||||
|
))
|
||||||
|
sub.add(PtVariable(
|
||||||
|
"particleDX",
|
||||||
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
|
ZeropageWish.DONTCARE,
|
||||||
|
0u,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
3u,
|
||||||
|
Position.DUMMY
|
||||||
|
))
|
||||||
|
sub.add(PtVariable(
|
||||||
|
"xx",
|
||||||
|
DataType.WORD,
|
||||||
|
ZeropageWish.DONTCARE,
|
||||||
|
0u,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Position.DUMMY
|
||||||
|
))
|
||||||
|
|
||||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||||
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
|
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
}
|
}
|
||||||
it.add(targetIdx)
|
it.add(targetIdx)
|
||||||
}
|
}
|
||||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||||
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
|
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
assign.add(target)
|
assign.add(target)
|
||||||
assign.add(value)
|
assign.add(value)
|
||||||
sub.add(assign)
|
sub.add(assign)
|
||||||
@ -80,7 +122,7 @@ class TestCodegen: FunSpec({
|
|||||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
numberAssign.add(numberAssignTarget)
|
numberAssign.add(numberAssignTarget)
|
||||||
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
|
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||||
sub.add(numberAssign)
|
sub.add(numberAssign)
|
||||||
|
|
||||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
@ -125,5 +167,15 @@ class TestCodegen: FunSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("memory mapped zp var is correctly considered to be zp var") {
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val st = SymbolTable(program)
|
||||||
|
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
|
||||||
|
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
|
||||||
|
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
|
||||||
|
allocator.isZpVar("zpmemvar") shouldBe true
|
||||||
|
allocator.isZpVar("normalmemvar") shouldBe false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
id "org.jetbrains.kotlin.jvm"
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
targetCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':codeCore')
|
|
||||||
implementation project(':intermediate')
|
|
||||||
implementation project(':codeGenIntermediate')
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
java {
|
|
||||||
srcDir "${project.projectDir}/src"
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
srcDir "${project.projectDir}/res"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
|
28
codeGenExperimental/build.gradle.kts
Normal file
28
codeGenExperimental/build.gradle.kts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":codeCore"))
|
||||||
|
implementation(project(":simpleAst"))
|
||||||
|
implementation(project(":intermediate"))
|
||||||
|
implementation(project(":codeGenIntermediate"))
|
||||||
|
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir(file("${project.projectDir}/src"))
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDir(file("${project.projectDir}/res"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: there are no unit tests in this module!
|
@ -13,5 +13,6 @@
|
|||||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||||
<orderEntry type="module" module-name="intermediate" />
|
<orderEntry type="module" module-name="intermediate" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
|
<orderEntry type="module" module-name="simpleAst" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.codegen.experimental
|
package prog8.codegen.experimental
|
||||||
|
|
||||||
|
import prog8.code.IAssemblyProgram
|
||||||
|
import prog8.code.ICodeGeneratorBackend
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyProgram
|
|
||||||
import prog8.code.core.ICodeGeneratorBackend
|
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
@ -21,11 +21,13 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
// but you can also use the Intermediate Representation to build a codegen on:
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
val irProgram = irCodeGen.generate()
|
val irProgram = irCodeGen.generate()
|
||||||
|
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
|
||||||
|
|
||||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
|
|
||||||
println("** experimental codegen stub: no assembly generated **")
|
if(!options.quiet)
|
||||||
|
println("** experimental codegen stub: no assembly generated **")
|
||||||
return EmptyProgram
|
return EmptyProgram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
private object EmptyProgram : IAssemblyProgram {
|
private object EmptyProgram : IAssemblyProgram {
|
||||||
override val name = "<Empty Program>"
|
override val name = "<Empty Program>"
|
||||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
println("** nothing assembled **")
|
if(!options.quiet)
|
||||||
|
println("** nothing assembled **")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user