mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
2716 Commits
version7.2
...
v11.1
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
8dbfb8ab76 | |||
e29ff1c848 | |||
585f6ffc9b | |||
46b94c17d6 | |||
7af8007447 | |||
16a2b2f566 | |||
ea2a90c3c5 | |||
5cda750e5e | |||
4e143d45c8 | |||
4c50980d81 | |||
2954f5f04d | |||
cac4c1eb1e | |||
0b1f30d98c | |||
c7b1e8d772 | |||
a4f7512d44 | |||
0d3ad80659 | |||
aba1a73e28 | |||
dca31b2ca3 | |||
0cb378ca31 | |||
cf551d2cc7 | |||
ac0c8a68f6 | |||
5986dcdd2f | |||
6be6eb2227 | |||
d34015eec5 | |||
255c5bfaca | |||
01c6754928 | |||
8eaf884f69 | |||
699a2bb7ab | |||
4a2dcd20d1 | |||
4e98fb75d6 | |||
64e66e732f | |||
7aec627f6b | |||
59a2fec176 | |||
edc5a5a94f | |||
c5b7edad82 | |||
124ffac4e4 | |||
6d2a36fb2b | |||
28b43b3e1d | |||
f7feaf158d | |||
2396f707c6 | |||
d4d8e1b1ba | |||
44fec2c729 | |||
a80a6913e3 | |||
0eac04c220 | |||
29dd758302 | |||
5c45adc7f0 | |||
ad22cf08cd | |||
2c2ae64194 | |||
97c2dadd16 | |||
b36e1e3baf | |||
2da35fec17 | |||
bdeac74cfc | |||
6516d7cb15 | |||
31cf76042d | |||
c4c4dcf2b3 | |||
03145630f8 | |||
e2fcac322f | |||
beaff4d650 | |||
79af96ddde | |||
e439720c9d | |||
48d0185ea4 | |||
e2592b4e0b | |||
2967866e3d | |||
b566ea5c3f | |||
8f6eaeac2c | |||
b4facaeb3c | |||
d12b7ccc6b | |||
453e8bd0a0 | |||
9204d390ae | |||
b70ce0015c | |||
d113827753 | |||
c67f877857 | |||
0ec719e429 | |||
17f7b11148 | |||
966b017670 | |||
4c98070b3c | |||
3681d6ee1c | |||
0af17cdc33 | |||
2aae1f5e30 | |||
d18f2a7bfd | |||
9046fe8d3a | |||
78c7ee247a | |||
d5adb85e5b | |||
69f953fd9b | |||
484677b4b1 | |||
b10a8e728f | |||
25f25a8767 | |||
0c053e4a2c | |||
3f6521cc9b | |||
a074491d5b | |||
a291164953 | |||
43c55b58d2 | |||
e7298f8162 | |||
ddf990296b | |||
ead8aa7800 | |||
7a9dd1ac9b | |||
1c97c22eff | |||
bbf621a8c4 | |||
8efa89165c | |||
4f8aaf9244 | |||
a97edef380 | |||
eefae24aa3 | |||
54bffc91ae | |||
63f5ef9e14 | |||
034f27a8dd | |||
c2f6311367 | |||
6f00a48772 | |||
b3dba67405 | |||
c9a4235669 | |||
ae0d52274c | |||
8973763866 | |||
3d799ae7fe | |||
8b10115390 | |||
d2e010c439 | |||
15867ab423 | |||
22c9e99fa3 | |||
ee262f6aad | |||
af64af2397 | |||
1feead2260 | |||
d3dcd24b4d | |||
fd1e6796ef | |||
3ea0f0cbaa | |||
f3e3311598 | |||
0dc50a93a4 | |||
fda8e61be4 | |||
ac1d4b4a7a | |||
c719e274d5 | |||
e4990f8ec5 | |||
62afd3342e | |||
6e8a89e6f1 | |||
aa2437cfb8 | |||
4a710ecdfc | |||
3ef5bdfeda | |||
7915dda35f | |||
9120e16683 | |||
a1ebc7090d | |||
054b4636e0 | |||
e3e7b060b7 | |||
5ac9c75521 | |||
07710e0995 | |||
d6a67f5f2b | |||
2675623aea | |||
94263c43d0 | |||
d8ec03874f | |||
a7247f5b8b | |||
4d37581694 | |||
5d7ddebcad | |||
53df0eb707 | |||
8babad9c7c | |||
8db7aa07bd | |||
42f4b06ac8 | |||
f4b50368ba | |||
db80417bd7 | |||
7a6f2ecc8c | |||
f5d556a7f9 | |||
2aae46d632 | |||
19ebc6d6b3 | |||
f88c29e083 | |||
6ed9899dc7 | |||
9de7698a5c | |||
112d2d6058 | |||
ddb8346711 | |||
8dd3faf395 | |||
35f3e8708b | |||
cfe3fcc9e7 | |||
66a6659a6e | |||
88ae3daa42 | |||
08b8fe01ab | |||
731132d4b3 | |||
98acff802f | |||
5f11f485a2 | |||
34f3169dda | |||
a3ef8f814b | |||
385dd6fc23 | |||
9af4168ae2 | |||
a5e0e31b74 | |||
b385dc8c26 | |||
92c012b55a | |||
641f6c05d8 | |||
788f6b44a6 | |||
63a4525f06 | |||
3e34a3ef72 | |||
0c5e8ca199 | |||
ff23fb0086 | |||
56f41d5e34 | |||
4700a239b9 | |||
bd5abfb969 | |||
b93fa75377 | |||
681ce9c60c | |||
dd0f0fe415 | |||
119040fc50 | |||
551e5688da | |||
56c1035581 | |||
ba1e907c79 | |||
2a3a27c56d | |||
647af34f5b | |||
993be6394e | |||
9a27505315 | |||
2e37f5dee3 | |||
03e486c082 | |||
edc83305a4 | |||
66e7c51064 | |||
60244aaf16 | |||
443391c700 | |||
47dbafacd4 | |||
5b6811d073 | |||
7516116bb7 | |||
e6014ea4dd | |||
362abfe284 | |||
ad4880997a | |||
592becc126 | |||
c38765301e | |||
5f27426f59 | |||
d924f8bff8 | |||
d14c61b160 | |||
fe2b67998c | |||
04df3c9f7f | |||
de3d0b40dc | |||
4db4a5f1b2 | |||
5a0524ff4d | |||
5b7801eea1 | |||
fbe231793b | |||
6a9269111e | |||
a94cfd34f5 | |||
28eae5a0fd | |||
1818738fc8 | |||
7e1e7a0780 | |||
1fc79ff6dd | |||
3535c1acda | |||
33c8caac8f | |||
51d708bbdd | |||
a5a918df84 | |||
820541e427 | |||
e63a8f0c01 | |||
c11a9b8709 | |||
80f39e8097 | |||
2a8b65e29c | |||
4bdf50145e | |||
3a9919a377 | |||
eef8ae00b8 | |||
ed15fac691 | |||
f739e679e4 | |||
fc0fae8caf | |||
f46896fd74 | |||
52649a8e4f | |||
bdfb01f6a0 | |||
1137e57393 | |||
267ea13e8c | |||
04f7b772a3 | |||
42c7569791 | |||
6d29b00a80 | |||
9f1bd2d7d6 | |||
9826d7c494 | |||
c6bf57b390 | |||
bfcf07c1a2 | |||
4d7e96d423 | |||
449461e412 | |||
607275ec66 | |||
e55cde2a81 | |||
84afb374e6 | |||
da1620807f | |||
f39ef8f565 | |||
fe8b6e820c | |||
f29d24e96a | |||
620ffe54ec | |||
ceaa4cd07d | |||
af17f903ee | |||
c532e28841 | |||
dba0846866 | |||
bed629998a | |||
bc2ede76bf | |||
2a1fec2ed2 | |||
004048e5a7 | |||
b941d6f1e4 | |||
37b346740b | |||
f5e332daf7 | |||
fe9a9fc5cb | |||
cc57477b99 | |||
a1574a7187 | |||
a5110b1f96 | |||
006713fe13 | |||
7868e672e0 | |||
e1a133c2c0 | |||
c77cd0da39 | |||
577333f2c4 | |||
7d8cdcbfea | |||
c5c4c6f111 | |||
73be754680 | |||
acd841dbb6 | |||
6b52ba9397 | |||
10d12f73d6 | |||
cd9119655c | |||
41afeccd51 | |||
6b87cbb703 | |||
32afcbfe42 | |||
bc2b38daf4 | |||
f40b7b62bb | |||
1ca3f64bf0 | |||
92527b4c1d | |||
c48012c385 | |||
a282b17286 | |||
58d9463f16 | |||
047decd552 | |||
82e0877e64 | |||
040d75dafa | |||
4e1686f6e3 | |||
b5e691f367 | |||
325f55f22d | |||
9724f2db7d | |||
5f20f321f0 | |||
d4b087ea3f | |||
8ff10724d1 | |||
1581381467 | |||
96b5a30f60 | |||
0e17a0474a | |||
b27368175d | |||
aba36f7c92 | |||
a3fa946300 | |||
01bbc2234e | |||
58e1864144 | |||
88458f5355 | |||
a4f697bae1 | |||
8201408f16 | |||
8b8caa1c2e | |||
4dc50cb551 | |||
5522a305ab | |||
d7f72056fc | |||
64c9c9b7fe | |||
98e1c843e4 | |||
906d9d858c | |||
16c1309df1 | |||
6eacf1bddd | |||
6c8c8e11cc | |||
e941d2665a | |||
68669dbef0 | |||
6a48de9a9f | |||
9d6d98930b | |||
3cc858db12 | |||
386a391fd9 | |||
d33aed4ed5 | |||
73ec8c31ad | |||
24944ad49e | |||
26ed231f61 | |||
8485b8429f | |||
358215e4dd | |||
f874942075 | |||
2cadb546d5 | |||
344a1b9eb8 | |||
3c77f8a020 | |||
8e00408e3e | |||
abcdfd8e28 | |||
b0f5b6925d | |||
ef79d0c43e | |||
78b4288005 | |||
680f5d21ee | |||
c71aa0895f | |||
9f8e61789a | |||
932035cdc5 | |||
ef198f1493 | |||
48ef856c0b | |||
9aea2b22c4 | |||
e0055bc431 | |||
9553248ed6 | |||
39d2194d8f | |||
0800033b47 | |||
64d8943b7d | |||
444e97b00b | |||
1816bda7ea | |||
d4a2031c07 | |||
8cf0b6cf51 | |||
f2010bf7a5 | |||
8f56a7fe69 | |||
64c132ee0a | |||
84a7e86fe3 | |||
a8c09d6144 | |||
87c46ba730 | |||
0f83dc6491 | |||
cc22861719 | |||
a14c192ea3 | |||
b3d98be862 | |||
43027a4728 | |||
03831a7394 | |||
fdbbd181ea | |||
69075376dc | |||
504d1440cc | |||
9e33b8b8da | |||
0cfcc5cd29 | |||
e0de662f8e | |||
66a836d094 | |||
80095f4962 | |||
828d83dbef | |||
7de665d1e4 | |||
0a356ba73a | |||
41de8caa13 | |||
968609d06d | |||
3b199a2a87 | |||
0c1018ec61 | |||
bc3f2db3de | |||
06bedb7adb | |||
45a9751217 | |||
e8da62aa29 | |||
ddb2ff4216 | |||
f27e3478b9 | |||
38dc7fb7bd | |||
aa4cd13c31 | |||
4a4b6c04a1 | |||
37fa3b34a2 | |||
f8084e7955 | |||
4d5119ce3e | |||
d85c347a6c | |||
7dd758a753 | |||
806654fc44 | |||
8e6b91cb9e | |||
334e6dca28 | |||
f2daa17b92 | |||
6d9fccacb1 | |||
37638e7ed0 | |||
8a0e650511 | |||
8ba5a0d90c | |||
bfd3edb617 | |||
56ba24962c | |||
19a2110ba2 | |||
242a3eec63 | |||
fee46f2e54 | |||
6aed7e429a | |||
517ea82b99 | |||
99c29343de | |||
892fa76883 | |||
d446b57d05 | |||
0e086d788b | |||
498841d45d | |||
d1f8ee1e56 | |||
07feb5c925 | |||
75fd263e85 | |||
1e1f444cab | |||
89cc7e5fa9 | |||
265e7aefbf | |||
1c55a6c6dc | |||
8f18b5b8a7 | |||
f790182f0b | |||
813007a5d8 | |||
d03ff1e4d0 | |||
932bbd0381 | |||
01bd648cb2 | |||
779a5606a7 | |||
ccc11e49d2 | |||
d28c994ecd | |||
5d88717f32 | |||
e35cfd4971 | |||
bcc4bf5c2b | |||
a0594cbce3 | |||
078bfefe41 | |||
9c1b11d605 | |||
44d82f9190 | |||
37fcde30d6 | |||
09c6cb4d6b | |||
b428343c2a | |||
dfce292294 | |||
2b8f613a00 | |||
2eb137618e | |||
4bb2b8ca9b | |||
5179562fb2 | |||
0a4de45453 | |||
ffdc658dc8 | |||
7530f4407b | |||
73864c8101 | |||
f948917124 | |||
0d44492086 | |||
38a22fbc99 | |||
8ae435549d | |||
9b113c0cbb | |||
0e0fac8c4b | |||
4cd9bb8f99 | |||
ad9eaeafeb | |||
6cd392909c | |||
49ec430592 | |||
09f3fbeb38 | |||
e7698686fa | |||
66d939df0d | |||
6bc079c7b7 | |||
299419917e | |||
69f6afe420 | |||
b7279a3d9e | |||
e14b854d7b | |||
8bd7c601c0 | |||
997288fa03 | |||
0f26b39997 | |||
ae66fcac1e | |||
43944a94eb | |||
eba0bde6f3 | |||
4544af441b | |||
a8be94de6b | |||
b24df31c2b | |||
332ba8ed7e | |||
58400f53bc | |||
01c2112881 | |||
a546c2247d | |||
0da9142009 | |||
796add0ee2 | |||
00b32f64e6 | |||
f97b3f23e2 | |||
08a079a96e | |||
e98e951834 | |||
2668bf8519 | |||
dd4c073e18 | |||
c7c72f00c7 | |||
ef1c665b9a | |||
d56565be25 | |||
e076b3aedc | |||
ae3b2ddf5f | |||
1bdc427d73 | |||
6a639ce533 | |||
d91ca8b197 | |||
a01c0a283d | |||
5c393091a0 | |||
01b680504b | |||
8e4319cd5a | |||
5a776dd690 | |||
cce08d95db | |||
28c1b208c1 | |||
3844bf1f72 | |||
745d192563 | |||
ee782e92ac | |||
afbc91d1fc | |||
f998888d6d | |||
7d8b42d63e | |||
6ebd4e821f | |||
d1806bfdc3 | |||
1d2d7155da | |||
b09e0a05bf | |||
c609e982fe | |||
2b227b43fe | |||
48f09f71ab | |||
ead8c59bda | |||
db52a9466c | |||
1509de390e | |||
88a1aa4f3d | |||
172e78e8f2 | |||
36bfef567d | |||
e40ebd75a2 | |||
992732f2cb | |||
b58a3ba1bb | |||
afe521b0c9 | |||
5d9caef45f | |||
278e2f5605 | |||
1e299bf360 | |||
8dfa0bc38c | |||
fde136fb7b | |||
ee4da1a757 | |||
ae2d96c455 | |||
6d8fbe0877 | |||
2fa1d8f2e8 | |||
533090a68e | |||
1dff59e1d6 | |||
44d232f52a | |||
5f6cff739a | |||
2764d235a9 | |||
45debff89f | |||
c45fbe6310 | |||
9ef9c24388 | |||
6a40f23578 | |||
6a0a6b4751 | |||
0bee6f6b41 | |||
82a15b5a16 | |||
11b7c4459e | |||
98570ac456 | |||
1b2296ad5b | |||
16851746d6 | |||
935450a45f | |||
ba67fd318b | |||
08ac459a41 | |||
a83e9d9a0a | |||
62d3f01948 | |||
af5ca2d0b8 | |||
ab4bcdf12d | |||
a6756d2cea | |||
f81061dd42 | |||
8e2c304b3c | |||
f21adaa3ef | |||
2637939e62 | |||
faf05582f8 | |||
161c02ced3 | |||
ff8de8e42d | |||
09d506194f | |||
42db3085df | |||
ad14c88fde | |||
0c9daf6eaf | |||
86c6530e46 | |||
159f80d629 | |||
aa949165c7 | |||
d22359b6e7 | |||
d73709653d | |||
405926e811 | |||
36758f41a4 | |||
7ebc9c79cf | |||
e0668b55b9 | |||
76c09da961 | |||
7e3b8c2c59 | |||
ecca854c7c | |||
3b0d7ea960 | |||
f70fa42eac | |||
5698de6cf4 | |||
c5a333a904 | |||
ff324955dd | |||
70436f5dca | |||
31177a2b1b | |||
4de012fc49 | |||
ee2888e744 | |||
efe4df92dc | |||
723ab54f97 | |||
d9389afc66 | |||
e7178ee496 | |||
d5f35bb3fb | |||
72f1a779f2 | |||
3277544295 | |||
98d2c64d5d | |||
f68b46fc60 | |||
d54ab856e7 | |||
16b24fadea | |||
b3803cbdf1 | |||
2ceaa25181 | |||
513611c5a6 | |||
7ec4ba40ad | |||
92374e122b | |||
94f12732ab | |||
0904712a00 | |||
32becdbced | |||
34aa21f7d9 | |||
cc81dd7d3e | |||
335213b55f | |||
13ab4166c0 | |||
3dc5a0e7f8 | |||
e15c5cde53 | |||
d88c09b098 | |||
893b383bdf | |||
dd7c9d62e6 | |||
97c5c90eff | |||
1fb94e7a7b | |||
daca87c6d0 | |||
203ec5fa46 | |||
9ea69c07b8 | |||
68539d6cc9 | |||
f75fd0811e | |||
836bc9d456 | |||
a37769aafe | |||
68e62e4bd2 | |||
a5cd3728c9 | |||
a48ce35f0b | |||
e1835b5775 | |||
433832b329 | |||
ee81da14d6 | |||
6395d1908e | |||
989a5a2f8a | |||
b7a622c68e | |||
a8507b437d | |||
e505bf9ccf | |||
a289b32053 | |||
c3f1f09ad1 | |||
70ee2026ff | |||
690782bf60 | |||
755cc4835e | |||
a684ea46e4 | |||
8fbe13f99d | |||
452e9e275f | |||
cd40088636 | |||
9b9e6f4af5 | |||
ae6eeadf54 | |||
5268b05060 | |||
390263a34e | |||
55646edc3e | |||
8d177beb78 | |||
1da0c59182 | |||
36e8f10d2b | |||
cdf5a8f20f | |||
eb64d92333 | |||
eb55da63ef | |||
918302f79b | |||
9d7131d9f6 | |||
229c1114dd | |||
885df9156f | |||
c319233ddc | |||
958b5c0780 | |||
880c0a5da8 | |||
237c6dc856 | |||
ccf6e32bf9 | |||
a1874f6f00 | |||
95e4490a8a | |||
31c132c2eb | |||
00b0ec58b4 | |||
a1d0e5bb65 | |||
03e0d4b2e8 | |||
6afdd4e6fd | |||
b500a0d477 | |||
dd2463a440 | |||
23a8bebd9e | |||
3caf9108ad | |||
bde4be8231 | |||
0bbbb12ed2 | |||
b570bdaed7 | |||
8c0843cc87 | |||
31458ffd81 | |||
c15c10a94e | |||
9fca978725 | |||
b125901717 | |||
eb018ae660 | |||
7e5a9474fe | |||
525a9b5036 | |||
c3fbdf34ca | |||
48bd51e1a5 | |||
10d0b03a90 | |||
e1b3582f08 | |||
95be1c9e22 | |||
1ce8fe06d5 | |||
15c649024e | |||
e97303c226 | |||
3b786c819d | |||
04959dbd8b | |||
5cd4b874ea | |||
f14ea1b3de | |||
9cc0cda0fb | |||
09a7a4bbe5 | |||
cfea8b3745 | |||
28bf0b61ce | |||
2dc2429735 | |||
83d4592526 | |||
2d528c26ae | |||
66b3dce794 | |||
93f77a1045 | |||
aa4d23a3d5 | |||
2d7ebff8e9 | |||
bad9dd3b3b | |||
2f4e517857 | |||
ff35ba3696 | |||
72768e7fad | |||
77f3852cdc | |||
66857ca477 | |||
75514fc7af | |||
be06d871b6 | |||
f98ee326b4 | |||
bc8126eb16 | |||
4c8beefdcb | |||
bbb6c53457 | |||
d8991894e3 | |||
c7b7dcfd03 | |||
2c9e50873c | |||
923367296d | |||
151a206617 | |||
e403c4cf99 | |||
e3fbe37f9f | |||
dc870cd5ea | |||
584be44743 | |||
5fffd35ec1 | |||
b92e22e4a6 | |||
3e6d16a7a8 | |||
ecbcc277b8 | |||
dff1d9e4dd | |||
7c0bde7310 | |||
a82d21ac05 | |||
0bf8378fcb | |||
017ef8a837 | |||
0d63cdcb96 | |||
68a6f99c9f | |||
60781bcfc4 | |||
77fa2e2722 | |||
c36afd872e | |||
7e58a4c130 | |||
19a4bf1088 | |||
9678bbae4b | |||
a4d093afa1 | |||
ba788bcf0f | |||
f2c62bee7e | |||
548721e306 | |||
1ae950a638 | |||
c9385e93fe | |||
9bb16e293c | |||
c223702ea0 | |||
9167ba499d | |||
2d7e95e1b6 | |||
0cba736446 | |||
0816a57032 | |||
a0ab0bd3e2 | |||
b89ad4b328 | |||
6cda76a116 | |||
c112b327ab | |||
46c12a8899 | |||
c5219dfb3f | |||
4a8ee6815a | |||
e1b6bb154a | |||
b19c282269 | |||
e520921746 | |||
970642244b | |||
3b90be2d9e | |||
2f756f1e3a | |||
78e84182f0 | |||
65a7a8caf8 | |||
4c6a2f5df9 | |||
fea297e409 | |||
7cf6aba625 | |||
3bbc00cc8c | |||
70ed2b4203 | |||
0adce9b9c6 | |||
0e781d18fa | |||
4575a8fffe | |||
10d0ff252b | |||
c7d54570cc | |||
7136b33f2e | |||
70a78e74f6 | |||
d5707b7bf3 | |||
9f247901d4 | |||
5659742d97 | |||
450eaf7c4a | |||
47485e4b49 | |||
64254e758d | |||
c1aa5d4e47 | |||
ab8173637a | |||
3841cef497 | |||
b717f1c7eb | |||
da57f76de3 | |||
4784f1c65a | |||
41af63b333 | |||
e2bb0de24d | |||
b791fae9ce | |||
6033a9e20c | |||
9e8c8973d8 | |||
3933bf5c1a | |||
708e296774 | |||
84925ab69c | |||
b3cb9b7fe2 | |||
9cb61fa34d | |||
7c219d235c | |||
6938c79f88 | |||
b8284a147d | |||
15ee90e99c | |||
795f80b4ec | |||
6b6427492d | |||
6055b8c3dc | |||
a98cb50d55 | |||
e98bbc1c52 | |||
7245aece4f | |||
60cbb02822 | |||
4e863ecdac | |||
5037033fcf | |||
e6b158bc97 | |||
4cc0dfa10b | |||
4ced8889d3 | |||
d26967a87d | |||
fc8955941b | |||
071a80360f | |||
d2154f5f2e | |||
334d382bfa | |||
90c4b00f74 | |||
71261525e8 | |||
3126959576 | |||
02e51d8282 | |||
ffb2027a19 | |||
70c9ab9074 | |||
6d1fdf1ba6 | |||
1f7180d9a8 | |||
b4e94ae4dd | |||
07c606bfc9 | |||
e705a8bd89 | |||
b3bdfb7f1f | |||
5af1aeb092 | |||
be64fa674a | |||
204f5591a9 | |||
ee3e3a3a40 | |||
f9200a2b75 | |||
f570b70827 | |||
0db141eeac | |||
acb2ee53bb | |||
c544b7f5ba | |||
c0024e97e5 | |||
bdf8aa9168 | |||
de5ce0f515 | |||
bb95484c8a | |||
cad18b8a3a | |||
0f6a98751a | |||
aac5a4c27f | |||
d3f6415387 | |||
04da44eb98 | |||
7649be97b1 | |||
c9ef777e0f | |||
c0cb2438d5 | |||
30c531b39e | |||
bf703a8a66 | |||
e7b631b087 | |||
a9f5dc036c | |||
0a83b51e00 | |||
eab63ecc6c | |||
b0794cf35e | |||
5b9e71a27d | |||
eae41de27d | |||
e9163aa3a7 | |||
8c617515ba | |||
04e4e71f2e | |||
a587482edf | |||
0aac9350d5 | |||
f56c12ee4e | |||
4bb9ae61f2 | |||
ff7f3484e4 | |||
5da3abe6b4 | |||
c0b398e0ce | |||
3de10adac2 | |||
1b573d6552 | |||
2a96f93919 | |||
c6b2639ca4 | |||
b9abf37a7e | |||
373cbb4144 | |||
a521982576 | |||
a77fde577c | |||
ea6926e57d | |||
ba25b7fee6 | |||
7ee162d98b | |||
380f557c45 | |||
1bdae53f4e | |||
9314c346da | |||
bfaad1388c | |||
0b580ad05d | |||
bb35a80177 | |||
24fc95ac81 | |||
8f864417c4 | |||
bb9d29b061 | |||
b9d8ec1463 | |||
1842a7660d | |||
5caa2f5536 | |||
d6078be8b7 | |||
cf60723f14 | |||
f7ff0a2b1d | |||
cc49664b2f | |||
99fe74f026 | |||
b021869eeb | |||
b8806d163b | |||
1116aae1de | |||
5e5f60253b | |||
bbc02752c9 | |||
9896bc110e | |||
ca60f8ecdd | |||
544acd1e35 | |||
6e07602d77 | |||
82898f7bba | |||
d61283a8bc | |||
1ee3f826cc | |||
4a00a5ba9e | |||
39eda67867 | |||
a99d38fdaa | |||
0eb2d437e2 | |||
3ac9036c79 | |||
c94e292176 | |||
91d87c2d9b | |||
ff472f69c0 | |||
e18119e24c | |||
4a592dc64c | |||
d9e13201dd | |||
5c75b19c5d | |||
52a77db60f | |||
0513c250fb | |||
48864ad6cf | |||
cdbccad21e | |||
e15bc68c9b | |||
8bffd7672d | |||
61df5b3060 | |||
b5255444cd | |||
0c94e377fc | |||
8e5c67b4b2 | |||
b24f2f1756 | |||
c69c17de42 | |||
061617122a | |||
125ce3240f | |||
7215efe167 | |||
06d1570142 | |||
093c370faa | |||
aec9574737 | |||
7ceb76cff5 | |||
300e2fe9f8 | |||
91e1643627 | |||
91421b0c62 | |||
40f611664f | |||
dcba4f4098 | |||
c098ad2b3b | |||
b43223cb7a | |||
e243531dab | |||
1af38e62bc | |||
f37f062cdc | |||
7e734214dc | |||
05d152746f | |||
dea7f37553 | |||
415c599310 | |||
70cd4fedbe | |||
1e6d7673bc | |||
b4963b725b | |||
0371ffa4ce | |||
6a664a7e15 | |||
88ce9300bc | |||
85cf0e311c | |||
0e3d75cfeb | |||
630c8a5faa | |||
905921a684 | |||
1e469b3b0f | |||
bff3c4f95c | |||
bd2bcb6994 | |||
4c8898a639 | |||
97df33ab1a | |||
ef46fb2685 | |||
d5d6dd3614 | |||
6c233c6a0a | |||
6db715d879 | |||
ab02e8a546 | |||
8cbfe64f19 | |||
fd1e9971e4 | |||
68336a76c5 | |||
393e914a86 | |||
ffb54110e9 | |||
533d825f1a | |||
c65279b672 | |||
f9926beeef | |||
add8a777d8 | |||
21bc505d85 | |||
3fc49c001e | |||
3d69a95c49 | |||
d81fdf6d6b | |||
87d3109ffb | |||
180dbbb521 | |||
24aac7cee5 | |||
53e18a5387 | |||
92062d056d | |||
06368ab0a1 | |||
38efe25c68 | |||
319079de7a | |||
025bf900a5 | |||
2885f4f7b1 | |||
c07eda15b1 | |||
4274296cf3 | |||
76a203d4df | |||
24f37e2062 | |||
f465b2e2a0 | |||
ce00e49a89 | |||
d494f9d66b | |||
c35a183a64 | |||
9cdd5fe7f2 | |||
c21428215e | |||
64d5af46f5 | |||
25846ea18a | |||
798383596d | |||
9ca71bc937 | |||
5407429ec0 | |||
ee5c94f6db | |||
91045afbee | |||
3f64782023 | |||
f8d35f9502 | |||
ea78d3ec9a | |||
e056a28316 | |||
0bea721c2e | |||
e1b89494d0 | |||
cd8e7f3912 | |||
50604c25c2 | |||
aa6b2357d8 | |||
5b2d29bef6 | |||
a296d26328 | |||
d01a26ec61 | |||
efd7d6f0c0 | |||
b55be093be | |||
7c1d5cadd7 | |||
dd1592b03b | |||
9b37ac483f | |||
090820958e | |||
ac21e1be5c | |||
5196443b26 | |||
c8531cbeb1 | |||
c560abedba | |||
9b952fbc44 | |||
ccdf05e922 | |||
c3d74f2ae9 | |||
f47498888c | |||
5665a7f0cb | |||
b8178c6c8d | |||
4a0f15eb88 | |||
c4f53fe525 | |||
8c93ec52de | |||
befe0fff2a | |||
b6a837cbea | |||
4861973899 | |||
c593e4b500 | |||
5bf78c20d4 | |||
5c672130e6 | |||
d8214d4f12 | |||
64d1f09ce0 | |||
47d0f0ea40 | |||
2d85fd093e | |||
d936568b76 | |||
4598a83e8e | |||
f4bf00ad31 | |||
07fde7f6cc | |||
729209574e | |||
f28206d989 | |||
0c81b32cac | |||
11216017cb | |||
a7b9f53967 | |||
1fa2e2e37d | |||
f67d5faeb7 | |||
5cbf859458 | |||
629ed74d09 | |||
ca2af2ca63 | |||
52ab089615 | |||
01461a196d | |||
04832f052a | |||
c8b2c8ae50 | |||
1b81c7fb22 | |||
9ccda0247e | |||
a7df4dcf25 | |||
d91f47c791 | |||
a9ac4e7f44 | |||
fc3ec57437 | |||
266f6ab919 | |||
6218c1c00b | |||
cc81d6fe82 | |||
69f9102f2d | |||
beb9275982 | |||
abe48713f2 | |||
82cfaf2fbb | |||
3d3bc4738f | |||
2d0746f5a4 | |||
9c71e2f1c8 | |||
134fd62da8 | |||
2afd283582 | |||
c66734bab0 | |||
8e56a61f95 | |||
d265271148 | |||
b40e397b28 | |||
35ff1d996a | |||
deea0b05cb | |||
c50c9ca545 | |||
a819b4a5a5 | |||
df2d7d4734 | |||
79ce4098cf | |||
374464a1f8 | |||
c8d0bf27af | |||
6e4ae034b2 | |||
52b560e72d | |||
9b971ad222 | |||
3613162d09 | |||
3a272e998d | |||
d4c750beb4 | |||
84b31e65e1 | |||
7b802bfd3d | |||
f9c4632b8d | |||
e4764cd8a6 | |||
dd78a3a686 | |||
94c06e13f4 | |||
e8bebe5a75 | |||
5b0e1b4f9e | |||
8c0a93779b | |||
9241479da4 | |||
8ffca93cd5 | |||
7fea0c124a | |||
20dbdb20d2 | |||
e6b8e2e8be | |||
7c5b7f77cc | |||
de84547a21 | |||
44676756ae | |||
b399b0f182 | |||
1152191f48 | |||
af1b07ad44 | |||
b8113fff1e | |||
ff6948cf2d | |||
fd25e85d59 | |||
c07cd72e85 | |||
e2c101206c | |||
92276b5769 | |||
a2133f61a8 | |||
199adbbcf0 | |||
dc316fd7b4 | |||
025183602f | |||
db4619a9d9 | |||
451e527b7c | |||
54dd3a00df | |||
03c5dab79d | |||
1fdee861e8 | |||
c12bf991b3 | |||
78a097585d | |||
39132327cc | |||
dc32318cec | |||
592f74124c | |||
e5e63cc5ac | |||
f40e0f786d | |||
ebd9f1471b | |||
d76547ead4 | |||
4600772e05 | |||
ed597423cd | |||
f20ca06f85 | |||
a636d3f394 | |||
043df18daa | |||
96996bf18e | |||
f350137a14 | |||
b7a6f3ec75 | |||
6c34672549 | |||
e779a07bce | |||
9a36e8ba3b | |||
c968bacb01 | |||
25199dfb43 | |||
48fed4e6fb | |||
fc253237c9 | |||
589948c7f4 | |||
7e69690605 | |||
95f498ba9b | |||
fd07ae5225 | |||
8acd94fc89 | |||
1436480eab | |||
448d176c24 | |||
fd269453a4 | |||
b3b380964c | |||
6e9025ebf2 | |||
3922691b3c | |||
0545b77cf4 | |||
6b3f39fa1a | |||
3114ab87dc | |||
00bc99cc7b | |||
540b3ae2f4 | |||
dbfe4140e1 | |||
d3675ec254 | |||
ded2483fc0 | |||
e62ea388e0 | |||
f20356e9be | |||
d282a2d846 | |||
4641ac46e7 | |||
ba9268a09e | |||
fb9902c536 | |||
5318ba6c6e | |||
fd5ebef488 | |||
d9e4f39ddc | |||
435b9d8973 | |||
0ea70ba656 | |||
92a07b87d2 | |||
c3c82282ba | |||
adc15c24ef | |||
dddf9a9396 | |||
9ca6860ffa | |||
f7dd388954 | |||
6012839f0e | |||
8e9cbab053 | |||
aaf375a57b | |||
3cce985f03 | |||
c59df6ec20 | |||
5c3f41f64d | |||
cf3523f49f | |||
db794752cb | |||
bceaebe856 | |||
3916de2921 | |||
9e0f8c1a97 | |||
0cbc56b82e | |||
b95608f68a | |||
b6e5dbd06c | |||
914f19be86 | |||
f09bcf3fcf | |||
d0b18dec8e | |||
75d486b124 | |||
4914609485 | |||
75bd66326a | |||
8f904f75bb | |||
549c598f51 | |||
ed68d604d6 | |||
f83752f43b | |||
86c22636eb | |||
30d20a453b | |||
fe29d8a23f | |||
694d088160 | |||
6aabbffc62 | |||
7b59bc8d12 | |||
79d0fb0b52 | |||
edf56d34f8 | |||
623329fb33 | |||
9f0074eef9 | |||
6733253826 | |||
f117805129 | |||
c75b1581d2 | |||
109e118aba | |||
201b77d5b6 | |||
a5ca08f33d | |||
86210c4513 | |||
988a3e4446 | |||
0f5cd22bb7 | |||
2f5bed36b3 | |||
5b6534bb28 | |||
e31e5b2477 | |||
07d5fafe2e | |||
e08da659e5 | |||
8a4979f44c | |||
e67464325f | |||
94c9b0d23b | |||
e9ec310d8a | |||
c78d1e3c39 | |||
e94319145f | |||
3f3b01b5f6 | |||
19a2791c65 | |||
4e8ccf0ef3 | |||
f1a7d5ecf7 | |||
8b05abb80d | |||
48c9349ce9 | |||
117d848466 | |||
9a2df072cc | |||
99c62aab36 | |||
224278e07a | |||
74b69e191e | |||
8cda8a727c | |||
a3c0c7c96f | |||
4403e4ed62 | |||
9b209823f6 | |||
b2cb125bd4 | |||
5e8f767642 | |||
6ee270d9d8 | |||
44fa309d20 | |||
58d88f3dd4 | |||
e980c23177 | |||
75224321bb | |||
801af05b20 | |||
7611dbbddc | |||
6d40ca15bc | |||
32c1c19224 | |||
bbf6357222 | |||
dc16629c24 | |||
3718b9d768 | |||
c25eb088ec | |||
3feb3e52f8 | |||
8e730ef93d | |||
e0913a39ab | |||
7a27fbc001 | |||
ee0dbdad35 | |||
9225f88f89 | |||
a04839dd6b | |||
002006517a | |||
f5b202d438 | |||
a7df094ff4 | |||
1e6fa77633 | |||
eb4cff202c | |||
7ee777f405 | |||
81bd5c784e | |||
b526e132a7 | |||
1860f66de5 | |||
ded9ada9bc | |||
d0e6a2eb8b | |||
4e103a1963 | |||
475e927178 | |||
ca7932c4f0 | |||
8ab47d3321 | |||
def7e87151 | |||
27568c2bef | |||
0694a187d7 | |||
832601b36b | |||
578969c34c | |||
d1d0115aed | |||
c89e6ebfab | |||
ca1089b881 | |||
a1d04f2aad | |||
bf0604133c | |||
a82b2da16e | |||
f2273c0acc | |||
17bedac96c | |||
4831fad27a | |||
5e896cf582 | |||
add3491c57 | |||
f470576822 | |||
10760a53a8 | |||
eee805183c | |||
b8fb391022 | |||
3c698f1584 | |||
2fad52d684 | |||
ec64a68a71 | |||
db55562f6a | |||
d8409a9d2b | |||
0d0ce6eec1 | |||
483f313eda | |||
7b6c742178 | |||
d4a35ba6ff | |||
68b112837a | |||
e2f20ebf94 | |||
f870e4965a | |||
7ebcb219d6 | |||
c21913a66b | |||
77e956a29f | |||
08275c406a | |||
2931e1b87b | |||
153b422496 | |||
0f6a6d6fea | |||
91fdb3e2d4 | |||
d8e87bd881 | |||
922033c1b2 | |||
df1793efbf | |||
836a2700f2 | |||
8f3aaf77a1 | |||
00c059e5b1 | |||
f4f355c74a | |||
b465fc5aaf | |||
2d78eaa48d | |||
d08451bccc | |||
d8e785aed0 | |||
267b6f49b5 | |||
e6688f4b9d | |||
9d7b9771c2 | |||
136a9a39de | |||
3dcf628fdb | |||
e614e9787a | |||
e426fc0922 | |||
5d4bfffc7e | |||
207cdaf7a4 | |||
7315b581ce | |||
38efaae7b2 | |||
469e042216 | |||
0f1a4b9d8f | |||
7303c00296 | |||
fc55b34d84 | |||
6f67fc0e02 | |||
562d722ad5 | |||
144c1ba3a6 | |||
06b032af91 | |||
3603140114 | |||
e094785cbd | |||
e7408224ac | |||
e67c05c274 | |||
b22804efaf | |||
890f55f91a | |||
cc5fc0b892 | |||
5efe2b027a | |||
5b6569d0f9 | |||
0eda7ac498 | |||
a5ef353484 | |||
67a36d8d31 | |||
7cc3cc3990 | |||
dc0edc4c2b | |||
71d2f091e5 | |||
c2f062a391 | |||
224f490455 | |||
5b35232ab4 | |||
6d6db70e42 | |||
6830e15b4e | |||
3f07cad35d | |||
e951340033 | |||
db8912a735 | |||
0e297731a3 | |||
f20c4f98ac | |||
05e60cc7c0 | |||
55b4469767 | |||
f15516e478 | |||
17ceadbadf | |||
8c25b2b316 | |||
8b1ae404a3 | |||
13534cd4a9 | |||
abfb345503 | |||
42ae935496 | |||
434515d957 | |||
094f7803b7 | |||
b0c7bad391 | |||
e9a4a905ef | |||
7b6cd0cfbe | |||
b718b12083 | |||
cfa7258ff4 | |||
b70e0a0870 | |||
da8eb464b8 | |||
8f9d1cfa30 | |||
585009ac5c | |||
30ee65fd14 | |||
76428b16f0 | |||
0d7b14e2d8 | |||
a9d19d02b3 | |||
adcbe55307 | |||
aa99a7df64 | |||
00afa1ce52 | |||
e94bf4c63c | |||
ec5adffdc2 | |||
733c17ad3a | |||
53b0b562e6 | |||
fabae6e970 | |||
a9f9c40d8a | |||
6fc89607d3 | |||
2340760f53 | |||
39d6d2857e | |||
7b722a0001 | |||
e7682119e0 | |||
af6be44676 | |||
5a8f97a0b6 | |||
0d4dd385b8 | |||
94f0f3e966 | |||
43e31765e5 | |||
7c1bdfe713 | |||
9f09784b55 | |||
e7a3a89bfb | |||
7ea7e63f44 | |||
1d2ce2cbeb | |||
06cf2e0bd7 | |||
9d219ae4b9 | |||
71f5a6c50e | |||
90b2be2bf4 | |||
db1aa8fcbd | |||
11c000f764 | |||
4d6dcbd173 | |||
0da117efd2 | |||
533c368e32 | |||
8883513b0e | |||
dcc9a71455 | |||
1a56743bb1 | |||
387a4b7c35 | |||
1d65d63bd9 | |||
dda19c29fe | |||
ca41669f4f | |||
0e1886e6bd | |||
c26e116f0e | |||
5c9c7f2c5e | |||
ca2fb6cef3 | |||
46dac909ef | |||
b1e4347e10 | |||
97aa91c75e | |||
4f8fb32136 | |||
e0fbce0087 | |||
fb22f78fb3 | |||
d6393cdbe5 | |||
5167fdb3f0 | |||
ab00822764 | |||
b4352ad38b | |||
d07d00fa41 | |||
11d87e4725 | |||
627ed51a1b | |||
c8f3bfa726 | |||
3091e3a1c8 | |||
2f3e7d1c27 | |||
0e831d4b92 | |||
7294ec9a3c | |||
e34bab9585 | |||
7dd14955c1 | |||
6428ced157 | |||
30a42ec1bd | |||
aacea3e9db | |||
6886b61186 | |||
0744c9fa29 | |||
502a665ffc | |||
3c315703c0 | |||
12ed07a607 | |||
101b33c381 | |||
97f4316653 | |||
b0704e86f0 | |||
a182b13e5a | |||
80b630a1e4 | |||
475efbe007 | |||
3ab5e5ac48 | |||
c6c5ff2089 | |||
176ec8ac7d | |||
dcdd4b3255 | |||
fc0a0105b3 | |||
f3960d21a8 | |||
a44d853c1b | |||
6b41734d6a | |||
c33dc0f3be | |||
bb5ffb24a8 | |||
a878c9a61d | |||
6454bf8ec4 | |||
40aa733ea7 | |||
f37a822725 | |||
f249ccd414 | |||
7ef4ddf0f3 | |||
d8e18df3a1 | |||
78d3d9d27d | |||
0aa0ec5abd | |||
b6eef3612f | |||
666d62dd7a | |||
44ee4b989f | |||
18790d867c | |||
d6b8936376 | |||
4d840c7db8 | |||
4d2b21816d | |||
2d34fdd28f | |||
68abda1219 | |||
f778f08f76 | |||
ac1bd2fb7b | |||
4b7b1379d9 | |||
e560e2ab3f | |||
1e441c2ddf | |||
93ce74eeb1 | |||
f718f4251b | |||
4644c9b621 | |||
197081f10d | |||
00b717cde8 | |||
34aa917ca4 | |||
a38ddcb364 | |||
5b9576df4e | |||
310219e5d7 | |||
a0deb463c9 | |||
90ddec2ad8 | |||
f6b03d5a78 | |||
f531daa872 | |||
046dceb5c2 | |||
dcc1f00048 | |||
05f935b598 | |||
f2d27403c5 | |||
473efbe67a | |||
aeabf0f324 | |||
80ab552ad8 | |||
7d4695c5b2 | |||
5189eaca36 | |||
cfb31377fc | |||
a07c52e112 | |||
8e1071aa89 | |||
7cb9a6ba60 | |||
350dc731f1 | |||
f690f58bd4 | |||
4bc65e9ef7 | |||
2d600da8b6 | |||
35af53828a | |||
10ddd5b127 | |||
f46e131f18 | |||
feb5c8be95 | |||
edf12bec71 | |||
ff1fc28287 | |||
314398ba4c | |||
840331347b | |||
6181b12ab8 | |||
68da661edc | |||
88cbb6913d | |||
7a26646e1b | |||
92eb3b0bf6 | |||
fb63434eee | |||
97f90d9684 | |||
f91786367f | |||
6a57337a68 | |||
211e2bb37a | |||
d2d08bf143 | |||
8acb37b6c2 | |||
81b3d2db4f | |||
9633c0b07a | |||
1dfa8ee7d8 | |||
1163543a98 | |||
bdb7de34be | |||
9500fc11ac | |||
65daf29acd | |||
298b25cf7d | |||
41f4e22a17 | |||
288c57c144 | |||
7ff8923569 | |||
b41779bd02 | |||
beea6bc794 | |||
fee58e98c5 | |||
c51c1da618 | |||
ea2812f50f | |||
3ec05709d5 | |||
4bdac7404a | |||
cc41218d37 | |||
4b336b1853 | |||
e1c77ce236 | |||
064d412ec8 | |||
7fff4f249d | |||
7a3745f642 | |||
f8658f6afa | |||
223b725a10 | |||
25aad8d7be | |||
b2c9b7635d | |||
24d13dd120 | |||
965340ff90 | |||
8e36fe6bef | |||
2eb41a8caf | |||
fb989ae62f | |||
7901ec2a64 | |||
f675dbc726 | |||
2ad4fdbbb9 | |||
97cb0cbd08 | |||
4ca0805de1 | |||
4b358abbb7 | |||
dc82a0fc16 | |||
435d6f6f3f | |||
ef92451d1a | |||
06184bdcb1 | |||
af98d01053 | |||
bb1cda0916 | |||
a6d0ea347c | |||
0fcd57192b | |||
a6ffa5738b | |||
c75bd97537 | |||
eea09f4de5 | |||
5656ec11d3 | |||
eb53e44cb0 | |||
69f3106062 | |||
8ab99f6129 | |||
53a3c59a91 | |||
df36983049 | |||
bda016bb3b | |||
cc174b7b85 | |||
bf9d120081 | |||
775c85fc18 | |||
5a756aaed9 | |||
dca092fd7c | |||
c6e92ecac4 | |||
93008ff605 | |||
43c7b935df | |||
8f9a0a244a | |||
fd13bd864e | |||
710f27afa9 | |||
f537793b0b | |||
f7183e38ee | |||
0a65dfdd10 | |||
3075578245 | |||
b042b7705e | |||
d56eb397f9 | |||
3054a1d32d | |||
0a3cd652b0 | |||
f70b914779 | |||
46ca0ac10d | |||
031f647952 | |||
8f1c86f550 | |||
926fdecd13 | |||
af2ca7a67e | |||
9e3e2ff81a | |||
a9fe6472d9 | |||
a862a81480 | |||
dbb92881a1 | |||
10bf7f5d07 | |||
1e61d84fd1 | |||
8618ba1b60 | |||
3c8c44155d | |||
2002412026 | |||
7f69517fd4 | |||
851f8645b5 | |||
c40cfaa388 | |||
0349d1d57c | |||
53049c02ee | |||
73a3a61729 | |||
5fe6aa2800 | |||
c7eafd7c79 | |||
10b5fb5d72 | |||
c4eaa944e2 | |||
a735939d1e | |||
6ed5f04970 | |||
b459b09b2f | |||
3f5877dbcc | |||
e659b91c4d | |||
e09f054058 | |||
b646f50265 | |||
0a48ef3030 | |||
ba614801ee | |||
fd6eb47e68 | |||
e69aeb8b98 | |||
26ea1da146 | |||
c9e8c7a290 | |||
5e4eb92443 | |||
461b6499ef | |||
c769920b6e | |||
181b98ef9e | |||
4e1184a400 | |||
e52d9e3210 | |||
dc6475c91b | |||
52f9956e92 | |||
0bf00d1ca4 | |||
d1a707df57 | |||
4dc9b45297 | |||
6e31eebfb5 | |||
a7df828932 | |||
517cf61d11 | |||
4be7bc8323 | |||
74c05d00a9 | |||
677613d30a | |||
bacba629a5 | |||
14e36f1362 | |||
d43ad849d1 | |||
627aa61184 | |||
dad5b17ac8 | |||
fef52c0112 | |||
8c4765b386 | |||
7c121bfc01 | |||
942c5cc04b | |||
348b3036ff | |||
09d3451d9d | |||
b1a49e5f29 | |||
da01a5b4dc | |||
3f9cdd9b56 | |||
0f9e87d7bb | |||
0869789214 | |||
10c8cc35c5 | |||
30c2e3e8ff | |||
86cc2f1075 | |||
fa357a450b | |||
b32641db87 | |||
0ee790969d | |||
7844ace934 | |||
f4993d6e5d | |||
0fab806f36 | |||
be2113d291 | |||
625d5b2313 | |||
6471c0c536 | |||
47c53fa60a | |||
cf50e4f6ec | |||
7eea97d741 | |||
88b55ab93e | |||
ee36d47c27 | |||
6f2fdbe447 | |||
0f36be0001 | |||
0f4a197e34 | |||
7dbff5b9e6 | |||
220246278a | |||
349e5a15e9 | |||
bf7f4bba7b | |||
ab1766a559 | |||
51bf33040a | |||
a2c7273801 | |||
ec6ac5bf24 | |||
ec7501782d | |||
890b1c2d52 | |||
c25d07259a | |||
c960246eee | |||
a01aee3111 | |||
e2e951efdf | |||
3f6393f732 | |||
b6eb343234 | |||
207a7e5160 | |||
a0face4a28 | |||
a8cf9f5cc4 | |||
461b38e653 | |||
8e4c0f7c22 | |||
d78bfcc35c | |||
2b7c09e6ee | |||
036d9dbe59 | |||
1d342cc6af | |||
62b32b2211 | |||
ae45ce517e | |||
5b3ccab7dc | |||
95f16c38a9 | |||
d616cb283b | |||
9874fe2c23 | |||
520a142992 | |||
6ff56dc0bb | |||
1e63615592 | |||
3e62ffed0a | |||
b133d51a83 | |||
037b89f018 | |||
20d06d9f9d | |||
156cf7315c | |||
e2886e5303 | |||
c6cf330e70 | |||
6be3b62d78 | |||
c57af5e81b | |||
f7431f809e | |||
ea43c34de8 | |||
fb6e9fa58f | |||
b2ce1e8029 | |||
d90c51220f | |||
d1b14b68fa | |||
d911728611 | |||
86a7200012 | |||
6ddb7453e1 | |||
ad2355f8d3 | |||
582c498fe3 | |||
0a0c58d450 | |||
0dc592b819 | |||
f46300016d | |||
3e1a7c6102 | |||
f07065bf84 | |||
6d79903eb3 | |||
e166329f34 | |||
bb1bf6a88c | |||
30cbb6c9a8 | |||
4e33ab1e89 | |||
5494f309c0 | |||
3b6e7eccdd | |||
e41d6787bb | |||
ed30108961 | |||
12712ef812 | |||
0307f6b42c | |||
3e44620966 | |||
7424f1f768 | |||
b5331d821c | |||
27f6d47efa | |||
dbc7ad2ec4 | |||
7b27d270a2 | |||
97b3a0b093 | |||
06b38506d1 | |||
fd581ffc37 | |||
ff57c5e9d3 | |||
9b16d7c786 | |||
4c1bb18956 | |||
7d2bf892b1 | |||
a99e77093f | |||
92737bb695 | |||
9b81955544 | |||
4a0031080a | |||
40e9fba312 | |||
e227cc92ff | |||
73dbdbcbe6 | |||
3961f26635 | |||
e51c274a18 | |||
e75d0c58a9 | |||
9a798360f4 | |||
844ad09464 | |||
1e1d1efd90 | |||
240e6835c2 | |||
61398ee8f8 | |||
e6e84859b7 | |||
abcdd331db | |||
775d136b91 | |||
dc93691fd9 | |||
48d782c69c | |||
0a04e626d7 | |||
e7c4bf5ebf | |||
546a416f7e | |||
179a7a2792 | |||
251b6fcf70 | |||
ab1fffb721 | |||
da352a322c | |||
7d20458e82 | |||
5a54066f81 | |||
a58e5a3399 | |||
9872f43cbf | |||
1078cc4642 | |||
db7ae028b2 | |||
2b6f5dbd59 | |||
f7aa0c45df | |||
a72d58cdf9 | |||
067283834a | |||
cf362c4a61 | |||
496245c801 | |||
859ab36347 | |||
1d740c7c36 | |||
a03c4c3659 | |||
094ecceaac | |||
2812736ae5 | |||
6f87f8706c | |||
38beebe720 | |||
fc1c3c6808 | |||
96ba895b84 | |||
df35dfe3bf | |||
c5504c6657 | |||
530e109433 | |||
6cce47b2f1 | |||
6185d5eca1 | |||
685ad1746e | |||
891f870ec0 | |||
ad9933f0f6 | |||
1b86117754 | |||
eeb3c968d6 | |||
406658a10f | |||
6a0551cea1 | |||
553f3b45d2 | |||
064a8e785c | |||
21e9723bb2 | |||
60b2c44a44 | |||
c4fe3ecc0a | |||
2f18a8f6d0 | |||
5ac784e18a | |||
7a2164b4d0 | |||
0a43eae184 | |||
3117e2b2a3 | |||
41fece4643 | |||
7aa807ec7f | |||
4d16e1e14a | |||
73fc18099e | |||
e34dac8dbb | |||
af0e7f7187 | |||
a3a6812608 | |||
2725c4ad4d | |||
c8cd6e9460 | |||
0cd27d6129 | |||
b47fc1c020 | |||
de6ef7ef5e | |||
f95fe8f1da | |||
bd0dee5db5 | |||
c13b7fd883 | |||
f7e74b3088 | |||
343f01d5e1 | |||
08bacdd090 | |||
41b1c80492 | |||
e5d7316e5d | |||
b043c3a6da | |||
98b2855b9c | |||
f3c52c409f | |||
1307bdc612 | |||
8c2e6971fc | |||
1903990f30 | |||
7d67005709 | |||
9acc2f92d1 | |||
72dfb0bda3 | |||
1635612430 | |||
abda837d2f | |||
101fb0b8aa | |||
10de7dc1f9 | |||
d2309b8114 | |||
6bdd81623f | |||
f538c9f0c3 | |||
8ae3bad6f7 | |||
77de99b383 | |||
312949f336 | |||
1ab635bd7e | |||
b35abd548c | |||
30e1c3307c | |||
08e052380a | |||
0e824c35cc | |||
548374ac2d | |||
9ad79fefc9 | |||
49d37c016e | |||
7c70c79a84 | |||
6916b8bff7 | |||
73dfb5f443 | |||
69b9dfa468 | |||
ab61b8ba0a | |||
5c8c64242f | |||
ddf96943f0 | |||
e773be2f58 | |||
f965804e6d | |||
ec078eba72 | |||
1815cb1bc3 | |||
7b3cd71085 | |||
06128b5d07 | |||
990c8e1f18 | |||
a170506356 | |||
5ecf2a3357 | |||
fa48746ba9 | |||
e2b8c069d7 | |||
14407bd1aa | |||
08db72903c | |||
46f9fab140 | |||
b7d06f2c0a | |||
118196a0bf | |||
586ce1fc80 | |||
adb979df38 | |||
3401cb5b4a | |||
ebf1f12e97 | |||
5766208207 | |||
4bf4771f08 | |||
0e87db9eb7 | |||
1e053783f3 | |||
7afc96112b | |||
7bb41a30ed | |||
3d1b0eb843 | |||
5b9af0b5ae | |||
9219ec539d | |||
c8bd57cd4d | |||
53bf8c09fd | |||
651c383668 | |||
9ed7587e3e | |||
674295e800 | |||
6b02f2eea0 | |||
5237e55326 | |||
3b59592110 | |||
72640ae058 | |||
d916027e75 | |||
8966d2aa06 | |||
de7ea04f54 | |||
bf71fabe0e | |||
b3368acb33 | |||
87220c6697 | |||
a3b5c2ad71 | |||
fb4c1473c5 | |||
2bb2502d20 | |||
fe51698579 | |||
0f0f40bff3 | |||
a798fe72d3 | |||
fba98d03a5 | |||
7dd2517f67 | |||
641477d6f6 | |||
8e56656c8d | |||
564a6a1f62 | |||
69f0c80cd7 | |||
6fcb51cea2 | |||
c58b8a4973 | |||
c8f4ab4f06 | |||
e425c4cca8 | |||
056ec986c2 | |||
de3b2fb95b | |||
789e39c719 | |||
b29c3152db | |||
3831679772 | |||
596f9566d8 | |||
124befe9d6 | |||
895534f32b | |||
50c16fe6de | |||
b092d1a5d3 | |||
a9b45630d7 | |||
c1a39c269e | |||
6fa3f0b6cd | |||
9e5e3d1559 | |||
7135205299 | |||
d99d977d2b | |||
7dd7e562bc | |||
75d857027e | |||
17694c1d01 | |||
749ad700d8 | |||
8f3df3039a | |||
02c315c194 | |||
b697375573 | |||
c57ef7725e | |||
3ae07503f2 | |||
9a0341adde | |||
96225efd96 | |||
c3bd904f41 | |||
74257163b1 | |||
7bc75fd220 | |||
a23281afab | |||
9e90dbdde6 | |||
1e8d8e40a2 | |||
583e208c1e | |||
9b91c427a1 | |||
196c5e9c24 | |||
d8f7feb672 | |||
7c889f17b9 | |||
c15a75556d | |||
5275c2e35f | |||
5267e06969 | |||
b62183adcb | |||
5d2dec1803 | |||
4a98dab948 | |||
9f8c70b326 | |||
a65404e63a | |||
05a1ddad05 | |||
4be3d63c0e | |||
de6ce4a46e | |||
7a9e5afb93 | |||
b2876b0a03 | |||
b66f66fe6a | |||
30f04962d4 | |||
0feeb88024 | |||
b799f2e05b | |||
56d21de001 | |||
7b54aa0c7d | |||
6e11b8ada1 | |||
98d25fc4e9 | |||
79405f47f6 | |||
1c7c4fc3b0 | |||
97e84d0977 | |||
9906b58818 | |||
371f084884 | |||
1c10839c14 | |||
c55fdd9834 | |||
67b0890a6e | |||
4da4f96669 | |||
60a64c2558 | |||
d4153da8b9 | |||
fc33ab8905 | |||
a123c64f59 | |||
a090fe3834 | |||
8fa84de28e | |||
3e3da38de1 | |||
a3be8ccc87 | |||
cdfef30c22 | |||
cabf1e82e8 | |||
608dc5e284 | |||
836d40072f | |||
431401d90e | |||
6da83e2bd7 | |||
7bccfc0006 | |||
e051e09c1d | |||
1462c57d0c | |||
77c2b2b326 | |||
3cf9b9d9a5 | |||
629117e594 | |||
08f87c321f | |||
1ff13723fe | |||
510bda1b28 | |||
890327b381 | |||
b21f7411dd | |||
5df623bd2e | |||
1e9d249f71 | |||
a7b5949e6a | |||
02010170ce | |||
35998142fe | |||
75ea453bf4 | |||
33061aaa0d | |||
e342311bef | |||
3d743a1ba1 | |||
abca618008 | |||
0d2c3901a3 | |||
d901a1531f | |||
d8d56b195f | |||
a52699717c | |||
98315de723 | |||
68d2f7d4c0 | |||
c812b5ee09 | |||
dcf487bdc1 | |||
547b1d3720 | |||
84f75f4156 | |||
ff69da3fa2 | |||
edffe92a24 | |||
b6fe40ada4 | |||
837804b231 | |||
81deed143b | |||
900cdd3fa1 | |||
0018dc6ce7 | |||
c92f914081 | |||
0498444ef2 | |||
ce3c34e458 | |||
20401b99d8 | |||
397f98513b | |||
e545ea9504 | |||
b867d8f731 | |||
9a68864b67 | |||
72d7178762 | |||
fbcd9a0c1d | |||
c3144a20db | |||
5b56e0462d | |||
b7fffbb6df | |||
1f346230e3 | |||
a2860a7c8c | |||
df997e5d3b | |||
a67a82c921 | |||
ea0fe8d3d2 | |||
2560042ac7 | |||
3d1d0696b9 | |||
83f893f50b | |||
9ecf95b075 | |||
7748c261da | |||
a2db44f80c | |||
b438d8aec0 | |||
4ac169b210 | |||
56dc6d7f1e | |||
45b8762188 | |||
cafab98d10 | |||
9256f910f0 | |||
32068a832a | |||
47c2c0376a | |||
f0dadc4a43 | |||
960b60cd2d | |||
d6abd72e55 | |||
0a568f2530 | |||
c52aa648c0 | |||
3d23b39f4c | |||
f3a4048ebf | |||
1b07637cc4 | |||
68b75fd558 | |||
7c5ec1853d | |||
e8f4686430 | |||
02348924d0 | |||
69dcb4dbda | |||
c838821615 | |||
8b4ac7801f | |||
64a411628d | |||
e8e25c6fd6 | |||
62485b6851 | |||
54025d2bf5 | |||
f5ebf79e71 | |||
66d5490702 | |||
42fe052f9f | |||
58d9c46a9b | |||
e4648e2138 | |||
110e047681 | |||
17d403d812 | |||
0a53bd4956 | |||
e52d05c7db | |||
b00db4f8a2 | |||
0c2f30fd45 | |||
e08871c637 | |||
ff715881bc | |||
0e2e5ffa52 | |||
8095c4c155 | |||
e86246a985 | |||
625aaa02eb | |||
787e35c9f3 | |||
8887e6af91 | |||
dde4c751da | |||
3c39baf1d6 | |||
b292124f3c | |||
c0035ba1a2 | |||
2491509c6a | |||
107935ed31 | |||
31491c62c5 | |||
eacf8b896a | |||
7936fc5bd8 | |||
adfaddbcf4 | |||
74db5c6be7 | |||
f9399bcce7 | |||
87600b23db | |||
cedfb17b18 | |||
fa4c83df6b | |||
42c8720e8b | |||
b334d89715 | |||
4f5d36a84d | |||
8f379e2262 | |||
fa11a6e18b | |||
52bedce8f4 | |||
4c82af36e6 | |||
dafa0d9138 | |||
2e0450d7ed | |||
6af3209d4d | |||
5d362047e2 | |||
f48d6ca9f8 | |||
964e8e0a17 | |||
1f60a2d8b9 | |||
5fd83f2757 | |||
c80df4140b | |||
53e1729e2f | |||
ab2d1122a9 | |||
5190594c8a | |||
c858ceeb58 | |||
f0f52b9166 | |||
00c6f74481 | |||
2177ba0ed2 | |||
3483515346 | |||
75a06d2a40 | |||
53ac11983b | |||
69f4a4d4f8 | |||
222bcb808f | |||
686483f51a | |||
8df3da11e3 | |||
84dafda0e4 | |||
b909facfe5 | |||
7780d94de1 | |||
f2c440e466 | |||
4937e004b5 | |||
4cb383dccb | |||
c8a4b6f23c | |||
857724c7e6 | |||
a9b0400d13 | |||
2d1e5bbc7e | |||
60627ce756 | |||
7961a09d16 | |||
613efcacc7 | |||
7e8db16e18 | |||
1fbbed7e23 | |||
984272beb4 | |||
b9ce94bb68 | |||
f4c4ee78d9 | |||
793596614e | |||
136280100c | |||
29f1e4d2c9 |
14
.github/FUNDING.yml
vendored
Normal file
14
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
#patreon: # Replace with a single Patreon username
|
||||||
|
#open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: irmen
|
||||||
|
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
#liberapay: # Replace with a single Liberapay username
|
||||||
|
#issuehunt: # Replace with a single IssueHunt username
|
||||||
|
#otechie: # Replace with a single Otechie username
|
||||||
|
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
custom: ['https://paypal.me/irmendejong']
|
41
.github/workflows/all-ci.yml
vendored
Normal file
41
.github/workflows/all-ci.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
name: Build and Test the Prog8 compiler
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: build and install recent 64tass
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y make build-essential
|
||||||
|
git clone --depth=1 https://github.com/irmen/64tass
|
||||||
|
cd 64tass
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: temurin
|
||||||
|
|
||||||
|
- name: Build and test with Gradle
|
||||||
|
run: |
|
||||||
|
./gradlew build shadowJar --no-daemon
|
||||||
|
sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt
|
||||||
|
|
||||||
|
- name: Create compiler shadowJar artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: prog8-compiler-jar-zipped
|
||||||
|
path: |
|
||||||
|
compiler/build/libs/*-all.jar
|
||||||
|
compiler/build/libs/hash.txt
|
||||||
|
|
9
.gitignore
vendored
9
.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,16 +15,17 @@ 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
|
||||||
|
compiler/src/prog8/buildversion/*
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg
|
*.egg
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.eggs/
|
.eggs/
|
||||||
/MANIFEST
|
/MANIFEST
|
||||||
.tox/
|
.tox/
|
||||||
|
.kotlin/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
parser.out
|
parser.out
|
||||||
parsetab.py
|
parsetab.py
|
||||||
@ -29,6 +34,6 @@ parsetab.py
|
|||||||
compiler/lib/
|
compiler/lib/
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
/prog8compiler.jar
|
|
||||||
sd*.img
|
sd*.img
|
||||||
*.d64
|
*.d64
|
||||||
|
|
||||||
|
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="1200" />
|
<option name="BUILD_PROCESS_HEAP_SIZE" value="3000" />
|
||||||
<bytecodeTargetLevel target="11" />
|
<bytecodeTargetLevel target="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
10
.idea/inspectionProfiles/Project_Default.xml
generated
10
.idea/inspectionProfiles/Project_Default.xml
generated
@ -7,7 +7,17 @@
|
|||||||
<language isEnabled="false" name="Groovy" />
|
<language isEnabled="false" name="Groovy" />
|
||||||
</Languages>
|
</Languages>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="1">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="sphinx_rtd_theme" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||||
<option name="processCode" value="false" />
|
<option name="processCode" value="false" />
|
||||||
<option name="processLiterals" value="true" />
|
<option name="processLiterals" value="true" />
|
||||||
|
19
.idea/kotlinc.xml
generated
Normal file
19
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Kotlin2JsCompilerArguments">
|
||||||
|
<option name="moduleKind" value="plain" />
|
||||||
|
</component>
|
||||||
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
|
<option name="jvmTarget" value="11" />
|
||||||
|
</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">
|
||||||
|
<option name="version" value="2.1.10" />
|
||||||
|
</component>
|
||||||
|
</project>
|
28
.idea/libraries/KotlinJavaRuntime.xml
generated
28
.idea/libraries/KotlinJavaRuntime.xml
generated
@ -1,19 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="KotlinJavaRuntime">
|
<library name="KotlinJavaRuntime" type="repository">
|
||||||
|
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-sources.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" />
|
||||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
|
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
11
.idea/libraries/antlr_antlr4.xml
generated
11
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,16 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="antlr.antlr4" type="repository">
|
<library name="antlr.antlr4" type="repository">
|
||||||
<properties maven-id="org.antlr:antlr4:4.9.2">
|
<properties maven-id="org.antlr:antlr4:4.13.2">
|
||||||
<exclude>
|
<exclude>
|
||||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||||
</exclude>
|
</exclude>
|
||||||
</properties>
|
</properties>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.9.2/antlr4-4.9.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.2/antlr4-4.13.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.9.2/antlr4-runtime-4.9.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.2/antlr-runtime-3.5.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3/ST4-4.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
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.23.1" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
@ -1,25 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
|
||||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
10
.idea/libraries/glassfish_javax_json.xml
generated
10
.idea/libraries/glassfish_javax_json.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="glassfish.javax.json" type="repository">
|
|
||||||
<properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
10
.idea/libraries/hamcrest.xml
generated
10
.idea/libraries/hamcrest.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="hamcrest" type="repository">
|
|
||||||
<properties maven-id="org.hamcrest:hamcrest:2.2" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
20
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
23
.idea/libraries/io_kotest_framework_datatest.xml
generated
Normal file
23
.idea/libraries/io_kotest_framework_datatest.xml
generated
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.framework.datatest" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-framework-datatest:5.9.1" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest/5.9.1/kotest-framework-datatest-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest-jvm/5.9.1/kotest-framework-datatest-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
41
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
41
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||||
|
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.1" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.9.1/kotest-runner-junit5-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.1/kotest-framework-engine-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.9.1/kotest-framework-discovery-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.1/kotest-extensions-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.1/kotest-framework-concurrency-jvm-5.9.1.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
@ -1,8 +1,8 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3" />
|
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.3/kotlinx-cli-jvm-0.3.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
17
.idea/libraries/junit_jupiter.xml
generated
17
.idea/libraries/junit_jupiter.xml
generated
@ -1,17 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="junit.jupiter" type="repository">
|
|
||||||
<properties maven-id="org.junit.jupiter:junit-jupiter:5.7.2" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.7.2/junit-jupiter-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.7.2/junit-jupiter-params-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.7.2/junit-jupiter-engine-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
21
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
21
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
@ -1,15 +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:1.1.12" />
|
<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/1.1.12/kotlin-result-jvm-1.1.12.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-jdk8/1.5.10/kotlin-stdlib-jdk8-1.5.10.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.5.10/kotlin-stdlib-1.5.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.10/kotlin-stdlib-jdk7-1.5.10.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.10/kotlin-stdlib-common-1.5.10.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>
|
11
.idea/libraries/slf4j_simple.xml
generated
11
.idea/libraries/slf4j_simple.xml
generated
@ -1,11 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="slf4j.simple" type="repository">
|
|
||||||
<properties maven-id="org.slf4j:slf4j-simple:1.7.30" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.30/slf4j-simple-1.7.30.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
13
.idea/libraries/takes.xml
generated
13
.idea/libraries/takes.xml
generated
@ -1,13 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="takes" type="repository">
|
|
||||||
<properties maven-id="org.takes:takes:1.19" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.19/takes-1.19.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.42/cactoos-0.42.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
12
.idea/misc.xml
generated
12
.idea/misc.xml
generated
@ -4,19 +4,25 @@
|
|||||||
<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>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.11" />
|
||||||
|
</component>
|
||||||
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
<type id="Python" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
14
.idea/modules.xml
generated
14
.idea/modules.xml
generated
@ -2,16 +2,22 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
|
<module fileurl="file://$PROJECT_DIR$/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$/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$/codeGenIntermediate/codeGenIntermediate.iml" filepath="$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" filepath="$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
27
.readthedocs.yaml
Normal file
27
.readthedocs.yaml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# .readthedocs.yaml
|
||||||
|
# Read the Docs configuration file
|
||||||
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||||
|
|
||||||
|
# Required
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
# Set the version of Python and other tools you might need
|
||||||
|
build:
|
||||||
|
os: ubuntu-22.04
|
||||||
|
tools:
|
||||||
|
python: "3.12"
|
||||||
|
|
||||||
|
# Optionally declare the Python requirements required to build your docs
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- requirements: docs/requirements.txt
|
||||||
|
|
||||||
|
# Build documentation in the docs/ directory with Sphinx
|
||||||
|
sphinx:
|
||||||
|
configuration: docs/source/conf.py
|
||||||
|
fail_on_warning: true
|
||||||
|
|
||||||
|
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||||
|
formats:
|
||||||
|
- pdf
|
||||||
|
- epub
|
@ -1,32 +0,0 @@
|
|||||||
#### Just a few remarks upfront:
|
|
||||||
* There is the (gradle/IDEA) module `parser`: that's the parser generated by ANTLR4, in Java. The only file to be edited here is the grammar, `prog8.g4`.
|
|
||||||
* Then we have the module `compilerAst` - in Kotlin - which uses `parser` and adds AST nodes. Here we put our additions to the generated thing, *including any tests of the parsing stage*.
|
|
||||||
- the name is a bit misleading, as this module isn't (or, resp. shouldn't be; see below) about *compiling*, only the parsing stage
|
|
||||||
- also, the tree that comes out isn't much of an *abstraction*, but rather still more or less a parse tree (this might very well change).
|
|
||||||
- **However, let's not *yet* rename the module.** We'll find a good name during refactoring.
|
|
||||||
|
|
||||||
#### Problems with `compilerAst`:
|
|
||||||
* `ModuleImporter.kt`, doing (Prog8-) module resolution. That's not the parser's job.
|
|
||||||
* `ParsingFailedError` (in `ModuleParsing.kt`): this exception (it is actually *not* a `java.lang.Error`...) is thrown in a number of places, where other exceptions would make more sense. For example: not finding a file should just yield a `NoSuchFileException`, not this one. The other problem with it is that it does not provide any additional information about the source of parsing error, in particular a `Position`.
|
|
||||||
* During parsing, character literals are turned into UBYTEs (since there is no basic type e.g. CHAR). That's bad because it depends on a specific character encoding (`IStringEncoding` in `compilerAst/src/prog8/ast/AstToplevel.kt`) of/for some target platform. Note that *strings* are indeed encoded later, in the `compiler` module.
|
|
||||||
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
|
|
||||||
|
|
||||||
#### Steps to take, in conceptual (!) order:
|
|
||||||
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
|
|
||||||
- from the local file system (use case: user programs)
|
|
||||||
- from resources (prog8lib)
|
|
||||||
- from plain strings (for testing)
|
|
||||||
2. add subclass `ParseError : ParsingFailedError` which adds information about the *source of parsing error* (`SourceCode` and `Position`). We cannot just replace `ParsingFailedError` right away because it is so widely used (even in the `compiler` module). Therefore we'll just subclass for the time being, add more and more tests requiring the new one to be thrown (or, resp., NOT to be thrown), and gradually transition.
|
|
||||||
3. introduce a minimal interface to the outside, input: `SourceCode`, output: a tree with a `Module` node as the root
|
|
||||||
- this will be the Kotlin singleton `Prog8Parser` with the main method `parseModule`
|
|
||||||
- plus, optionally, method's for registering/unregistering a listener with the parser
|
|
||||||
- the *only* exception ever thrown / reported to listeners (TBD) will be `ParseError`
|
|
||||||
- anything related to the lexer, error strategies, character/token streams is hidden from the outside
|
|
||||||
- to make a clear distinction between the *generated* parser (and lexer) vs. `Prog8Parser`, and to discourage directly using the generated stuff, we'll rename the existing `prog8Parser`/`prog8Lexer` to `Prog8ANTLRParser` and `Prog8ANTLRLexer` and move them to package `prog8.parser.generated`
|
|
||||||
4. introduce AST node `CharLiteral` and keep them until after identifier resolution and type checking; insert there an AST transformation step that turns them in UBYTE constants (literals)
|
|
||||||
5. remove uses of `IStringEncoding` from module `compilerAst` - none should be necessary anymore
|
|
||||||
6. move `IStringEncoding` to module `compiler`
|
|
||||||
7. same with `ModuleImporter`, then rewrite that (addressing #46)
|
|
||||||
8. refactor AST nodes and grammar: less generated parse tree nodes (`XyzContext`), less intermediary stuff (private classes in `Antrl2Kotlin.kt`), more compact code. Also: nicer names such as simply `StringLiteral` instead of `StringLiteralValue`
|
|
||||||
9. re-think `IStringEncoding` to address #38
|
|
||||||
|
|
7
LICENSE
7
LICENSE
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
This sofware license is for Prog8 the compiler + associated libraries.
|
This sofware license is for Prog8 the compiler + associated library files.
|
||||||
The software generated by running the compiler is excluded from this.
|
|
||||||
|
Exception: All output files generated by the compiler (intermediary files
|
||||||
|
and compiled binary programs) are excluded from this; you can do with those
|
||||||
|
whatever you want.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler
|
||||||
|
|
||||||
|
.PHONY: all test
|
||||||
|
|
||||||
|
all:
|
||||||
|
gradle installdist installshadowdist
|
||||||
|
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||||
|
|
||||||
|
test:
|
||||||
|
gradle build
|
||||||
|
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||||
|
|
90
README.md
90
README.md
@ -1,3 +1,7 @@
|
|||||||
|
[](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
|
||||||
@ -9,41 +13,72 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
|
|||||||
as used in many home computers from that era. It is a medium to low level programming language,
|
as used in many home computers from that era. It is a medium to low level programming language,
|
||||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||||
|
|
||||||
|
**Want to buy me a coffee or a pizza perhaps?**
|
||||||
|
|
||||||
|
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||||
|
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||||
|
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||||
https://prog8.readthedocs.io/
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
|
How to get it/build it
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Download the latest [official release](https://github.com/irmen/prog8/releases) from github.
|
||||||
|
- Or, if you want/need a bleeding edge development version, you can:
|
||||||
|
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
|
||||||
|
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
|
||||||
|
Note that if you are not using *gradle* to build it, you might have to perform some manual
|
||||||
|
tasks once to make it compile fully. These are explained in the linked instructions.
|
||||||
|
- Alternatively, you can also install the compiler as a package on some linux distros:
|
||||||
|
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
|
||||||
|
|
||||||
|
Community
|
||||||
|
---------
|
||||||
|
Most of the development on Prog8 and the use of it is currently centered around
|
||||||
|
the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel
|
||||||
|
dedicated to Prog8. Other than that, use the issue tracker on github.
|
||||||
|
|
||||||
|
|
||||||
Software license
|
Software license
|
||||||
----------------
|
----------------
|
||||||
GNU GPL 3.0, see file LICENSE
|
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||||
|
|
||||||
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
- The compiler and its libraries are free to use according to the terms of the GNU GPL 3.0
|
||||||
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
- *exception:* the resulting files (intermediate source codes and resulting binary program) created by the compiler
|
||||||
|
are excluded from the GPL and are free to use in whatever way desired, commercially or not.
|
||||||
|
|
||||||
|
|
||||||
What does Prog8 provide?
|
What does Prog8 provide?
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- reduction of source code length over raw assembly
|
- all advantages of a higher level language over having to write assembly code manually
|
||||||
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
- programs run very fast because it's compiled to native machine code
|
||||||
- modularity, symbol scoping, subroutines
|
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||||
|
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- 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
|
||||||
- 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
|
||||||
- small program boilerplate/compilersupport overhead
|
|
||||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
- conditional branches
|
- conditional branches that map 1:1 to cpu status flags
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- ``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``
|
||||||
- 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 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:*
|
||||||
|
|
||||||
@ -52,11 +87,15 @@ What does Prog8 provide?
|
|||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
|
|
||||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||||
|
|
||||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
- "c64": Commodore-64 (6502 like CPU)
|
||||||
|
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||||
|
- "pet32": Commodore PET (limited support)
|
||||||
|
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||||
|
- "neo": Neo6502 (experimental)
|
||||||
|
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +112,12 @@ IntelliJ IDEA with the Kotlin plugin).
|
|||||||
|
|
||||||
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
and a recent emulator version (R42 or newer) for the CommanderX16, such as [x16emu](https://cx16forum.com/forum/viewforum.php?f=30)
|
||||||
|
(preferred, this is the official emulator. If required, source code is [here](https://github.com/X16Community/x16-emulator/)).
|
||||||
|
There is also [Box16](https://github.com/indigodarkwolf/box16) which has powerful debugging features.
|
||||||
|
|
||||||
|
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||||
|
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
|
||||||
|
|
||||||
|
|
||||||
Example code
|
Example code
|
||||||
@ -85,12 +129,11 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
bool[256] sieve
|
||||||
ubyte[256] sieve
|
|
||||||
ubyte candidate_prime = 2 ; is increased in the loop
|
ubyte candidate_prime = 2 ; is increased in the loop
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
sys.memset(sieve, 256, false) ; clear the sieve
|
sys.memset(sieve, 256, 0) ; clear the sieve
|
||||||
txt.print("prime numbers up to 255:\n\n")
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
repeat {
|
repeat {
|
||||||
@ -126,9 +169,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when compiled an ran on a C-64 you'll get:
|
when compiled an ran on a C-64 you'll get:
|
||||||
|
|
||||||

|

|
||||||
|
@ -6,14 +6,19 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="compiler" />
|
|
||||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||||
<orderEntry type="library" name="glassfish.javax.json" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="takes" level="project" />
|
<orderEntry type="module-library">
|
||||||
<orderEntry type="library" name="slf4j.simple" level="project" />
|
<library>
|
||||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
<CLASSES>
|
||||||
|
<root url="jar://$MODULE_DIR$/lib/bsh-3.0.0-SNAPSHOT.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</orderEntry>
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</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
|
||||||
|
void 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
|
||||||
|
void 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
|
||||||
|
}
|
||||||
|
}
|
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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
benchmark-program/benchmark.p8
Normal file
112
benchmark-program/benchmark.p8
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
; 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
|
||||||
|
|
||||||
|
%zeropage basicsafe
|
||||||
|
%option no_sysinit
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
str[20] benchmark_names
|
||||||
|
uword[20] benchmark_score
|
||||||
|
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubyte benchmark_number
|
||||||
|
|
||||||
|
void 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++
|
||||||
|
|
||||||
|
benchmark_names[benchmark_number] = 0
|
||||||
|
benchmark_score[benchmark_number] = 0
|
||||||
|
|
||||||
|
void 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
|
||||||
|
void 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.10"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
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!
|
@ -6,11 +6,9 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="github.hypfvieh.dbus.java" level="project" />
|
|
||||||
<orderEntry type="library" name="slf4j.simple" level="project" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,8 +1,7 @@
|
|||||||
package prog8
|
package prog8.code
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By convention, the right side of an `Either` is used to hold successful values.
|
* By convention, the right side of an `Either` is used to hold successful values.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
sealed class Either<out L, out R> {
|
sealed class Either<out L, out R> {
|
||||||
|
|
286
codeCore/src/prog8/code/SymbolTable.kt
Normal file
286
codeCore/src/prog8/code/SymbolTable.kt
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.ast.PtAsmSub
|
||||||
|
import prog8.code.ast.PtNode
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
const val internedStringsModuleName = "prog8_interned_strings"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree structure containing all symbol definitions in the program
|
||||||
|
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||||
|
*/
|
||||||
|
class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) {
|
||||||
|
/**
|
||||||
|
* The table as a flat mapping of scoped names to the StNode.
|
||||||
|
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||||
|
*/
|
||||||
|
|
||||||
|
private var cachedFlat: Map<String, StNode>? = null
|
||||||
|
|
||||||
|
val flat: Map<String, StNode> get() {
|
||||||
|
if(cachedFlat!=null)
|
||||||
|
return cachedFlat!!
|
||||||
|
|
||||||
|
val result = mutableMapOf<String, StNode>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
result[child.value.scopedName] = child.value
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
cachedFlat = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetCachedFlat() {
|
||||||
|
cachedFlat = null
|
||||||
|
}
|
||||||
|
|
||||||
|
val allVariables: Collection<StStaticVariable> by lazy {
|
||||||
|
// can't be done with a generic function because those don't support local recursive functions yet
|
||||||
|
val vars = mutableListOf<StStaticVariable>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.STATICVAR)
|
||||||
|
vars.add(child.value as StStaticVariable)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
||||||
|
// can't be done with a generic function because those don't support local recursive functions yet
|
||||||
|
val vars = mutableListOf<StMemVar>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.MEMVAR)
|
||||||
|
vars.add(child.value as StMemVar)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||||
|
// can't be done with a generic function because those don't support local recursive functions yet
|
||||||
|
val vars = mutableListOf<StMemorySlab>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.MEMORYSLAB)
|
||||||
|
vars.add(child.value as StMemorySlab)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
|
|
||||||
|
fun getLength(name: String): Int? {
|
||||||
|
return when(val node = flat[name]) {
|
||||||
|
is StMemVar -> node.length
|
||||||
|
is StMemorySlab -> node.size.toInt()
|
||||||
|
is StStaticVariable -> node.length
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class StNodeType {
|
||||||
|
GLOBAL,
|
||||||
|
// MODULE, // not used with current scoping rules
|
||||||
|
BLOCK,
|
||||||
|
SUBROUTINE,
|
||||||
|
EXTSUB,
|
||||||
|
LABEL,
|
||||||
|
STATICVAR,
|
||||||
|
MEMVAR,
|
||||||
|
CONSTANT,
|
||||||
|
BUILTINFUNC,
|
||||||
|
MEMORYSLAB
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open class StNode(val name: String,
|
||||||
|
val type: StNodeType,
|
||||||
|
val astNode: PtNode?,
|
||||||
|
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||||
|
) {
|
||||||
|
|
||||||
|
lateinit var parent: StNode
|
||||||
|
|
||||||
|
val scopedName: String by lazy { scopedNameList.joinToString(".") }
|
||||||
|
|
||||||
|
open fun lookup(scopedName: String) =
|
||||||
|
lookup(scopedName.split('.'))
|
||||||
|
|
||||||
|
fun lookupUnscopedOrElse(name: String, default: () -> StNode) =
|
||||||
|
lookupUnscoped(name) ?: default()
|
||||||
|
|
||||||
|
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
|
||||||
|
lookup(scopedName.split('.')) ?: default()
|
||||||
|
|
||||||
|
fun lookupUnscoped(name: String): StNode? {
|
||||||
|
// first consider the builtin functions
|
||||||
|
var globalscope = this
|
||||||
|
while(globalscope.type!= StNodeType.GLOBAL)
|
||||||
|
globalscope = globalscope.parent
|
||||||
|
val globalNode = globalscope.children[name]
|
||||||
|
if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC)
|
||||||
|
return globalNode
|
||||||
|
|
||||||
|
// search for the unqualified name in the current scope or its parent scopes
|
||||||
|
var scope=this
|
||||||
|
while(true) {
|
||||||
|
val node = scope.children[name]
|
||||||
|
if(node!=null)
|
||||||
|
return node
|
||||||
|
if(scope.type== StNodeType.GLOBAL)
|
||||||
|
return null
|
||||||
|
else
|
||||||
|
scope = scope.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(child: StNode) {
|
||||||
|
children[child.name] = child
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
private val scopedNameList: List<String> by lazy {
|
||||||
|
if(type==StNodeType.GLOBAL)
|
||||||
|
emptyList()
|
||||||
|
else
|
||||||
|
parent.scopedNameList + name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookup(scopedName: List<String>): StNode? {
|
||||||
|
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||||
|
var node = this
|
||||||
|
while(node.type!=StNodeType.GLOBAL)
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
for(name in scopedName) {
|
||||||
|
if(name in node.children)
|
||||||
|
node = node.children.getValue(name)
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StStaticVariable(name: String,
|
||||||
|
val dt: DataType,
|
||||||
|
val initializationStringValue: StString?,
|
||||||
|
val initializationArrayValue: StArray?,
|
||||||
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
|
val zpwish: ZeropageWish, // used in the variable allocator
|
||||||
|
val align: Int,
|
||||||
|
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||||
|
|
||||||
|
var initializationNumericValue: Double? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setOnetimeInitNumeric(number: Double) {
|
||||||
|
// In certain cases the init value of an existing var should be updated,
|
||||||
|
// so we can't ask this as a constructor parameter.
|
||||||
|
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
|
||||||
|
// Certain codegens might want to put them back into the variable directly.
|
||||||
|
// For strings and arrays this doesn't occur - these are always already specced at creation time.
|
||||||
|
initializationNumericValue = number
|
||||||
|
}
|
||||||
|
|
||||||
|
val uninitialized: Boolean
|
||||||
|
get() = initializationArrayValue==null && initializationStringValue==null && initializationNumericValue==null
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(length!=null) {
|
||||||
|
require(initializationNumericValue == null)
|
||||||
|
if(initializationArrayValue!=null)
|
||||||
|
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
|
||||||
|
}
|
||||||
|
if(initializationNumericValue!=null) {
|
||||||
|
require(dt.isNumericOrBool)
|
||||||
|
}
|
||||||
|
if(initializationArrayValue!=null) {
|
||||||
|
require(dt.isArray)
|
||||||
|
require(length == initializationArrayValue.size)
|
||||||
|
}
|
||||||
|
if(initializationStringValue!=null) {
|
||||||
|
require(dt.isString)
|
||||||
|
require(length == initializationStringValue.first.length + 1)
|
||||||
|
}
|
||||||
|
if(align > 0) {
|
||||||
|
require(dt.isString || dt.isArray)
|
||||||
|
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) :
|
||||||
|
StNode(name, StNodeType.CONSTANT, astNode)
|
||||||
|
|
||||||
|
|
||||||
|
class StMemVar(name: String,
|
||||||
|
val dt: DataType,
|
||||||
|
val address: UInt,
|
||||||
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
|
astNode: PtNode?) :
|
||||||
|
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||||
|
|
||||||
|
init{
|
||||||
|
require(!dt.isString)
|
||||||
|
if(dt.isStringly && !dt.isWord)
|
||||||
|
requireNotNull(length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StMemorySlab(
|
||||||
|
name: String,
|
||||||
|
val size: UInt,
|
||||||
|
val align: UInt,
|
||||||
|
astNode: PtNode?
|
||||||
|
):
|
||||||
|
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||||
|
|
||||||
|
|
||||||
|
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
|
||||||
|
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||||
|
|
||||||
|
|
||||||
|
class StExtSub(name: String,
|
||||||
|
val address: PtAsmSub.Address?, // null in case of asmsub, specified in case of extsub.
|
||||||
|
val parameters: List<StExtSubParameter>,
|
||||||
|
val returns: List<StExtSubParameter>,
|
||||||
|
astNode: PtNode) :
|
||||||
|
StNode(name, StNodeType.EXTSUB, astNode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class StSubroutineParameter(val name: String, val type: DataType, val register: RegisterOrPair?)
|
||||||
|
class StExtSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||||
|
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) {
|
||||||
|
init {
|
||||||
|
if(number!=null) require(addressOfSymbol==null && boolean==null)
|
||||||
|
if(addressOfSymbol!=null) require(number==null && boolean==null)
|
||||||
|
if(boolean!=null) require(addressOfSymbol==null && number==null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias StString = Pair<String, Encoding>
|
||||||
|
typealias StArray = List<StArrayElement>
|
214
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
214
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
|
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
|
||||||
|
fun make(): SymbolTable {
|
||||||
|
val st = SymbolTable(program)
|
||||||
|
|
||||||
|
BuiltinFunctions.forEach {
|
||||||
|
val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED)
|
||||||
|
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY)))
|
||||||
|
}
|
||||||
|
|
||||||
|
val scopestack = ArrayDeque<StNode>()
|
||||||
|
scopestack.add(st)
|
||||||
|
program.children.forEach {
|
||||||
|
addToSt(it, scopestack)
|
||||||
|
}
|
||||||
|
require(scopestack.size==1)
|
||||||
|
|
||||||
|
if(options.compTarget.name != VMTarget.NAME) {
|
||||||
|
listOf(
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), options.compTarget.zeropage.SCRATCH_B1, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE), options.compTarget.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD), options.compTarget.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD), options.compTarget.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||||
|
).forEach {
|
||||||
|
it.parent = program
|
||||||
|
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addToSt(node: PtNode, scope: ArrayDeque<StNode>) {
|
||||||
|
val stNode = when(node) {
|
||||||
|
is PtAsmSub -> {
|
||||||
|
val parameters = node.parameters.map { StExtSubParameter(it.first, it.second.type) }
|
||||||
|
val returns = node.returns.map { StExtSubParameter(it.first, it.second) }
|
||||||
|
StExtSub(node.name, node.address, parameters, returns, node)
|
||||||
|
}
|
||||||
|
is PtBlock -> {
|
||||||
|
StNode(node.name, StNodeType.BLOCK, node)
|
||||||
|
}
|
||||||
|
is PtConstant -> {
|
||||||
|
require(node.type.isNumericOrBool)
|
||||||
|
StConstant(node.name, node.type.base, node.value, node)
|
||||||
|
}
|
||||||
|
is PtLabel -> {
|
||||||
|
StNode(node.name, StNodeType.LABEL, node)
|
||||||
|
}
|
||||||
|
is PtMemMapped -> {
|
||||||
|
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
||||||
|
}
|
||||||
|
is PtSub -> {
|
||||||
|
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
|
||||||
|
StSub(node.name, params, node.returns, node)
|
||||||
|
}
|
||||||
|
is PtVariable -> {
|
||||||
|
val initialNumeric: Double?
|
||||||
|
val initialString: StString?
|
||||||
|
val initialArray: StArray?
|
||||||
|
val numElements: Int?
|
||||||
|
val value = node.value
|
||||||
|
if(value!=null) {
|
||||||
|
when (value) {
|
||||||
|
is PtString -> {
|
||||||
|
initialString = StString(value.value, value.encoding)
|
||||||
|
initialArray = null
|
||||||
|
initialNumeric = null
|
||||||
|
numElements = value.value.length + 1 // include the terminating 0-byte
|
||||||
|
}
|
||||||
|
is PtArray -> {
|
||||||
|
initialArray = makeInitialArray(value)
|
||||||
|
initialString = null
|
||||||
|
initialNumeric = null
|
||||||
|
numElements = initialArray.size
|
||||||
|
require(node.arraySize?.toInt()==numElements)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
require(value is PtNumber)
|
||||||
|
initialString = null
|
||||||
|
initialArray = null
|
||||||
|
val number = value.number
|
||||||
|
initialNumeric = number
|
||||||
|
numElements = node.arraySize?.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialNumeric = null
|
||||||
|
initialArray = null
|
||||||
|
initialString = null
|
||||||
|
numElements = node.arraySize?.toInt()
|
||||||
|
}
|
||||||
|
// if(node.type in SplitWordArrayTypes) {
|
||||||
|
// ... split array also add _lsb and _msb to symboltable?
|
||||||
|
// }
|
||||||
|
val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align.toInt(), node)
|
||||||
|
if(initialNumeric!=null)
|
||||||
|
stVar.setOnetimeInitNumeric(initialNumeric)
|
||||||
|
stVar
|
||||||
|
}
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
if(node.name=="memory") {
|
||||||
|
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||||
|
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||||
|
val slabname = (node.args[0] as PtString).value
|
||||||
|
val size = (node.args[1] as PtNumber).number.toUInt()
|
||||||
|
val align = (node.args[2] as PtNumber).number.toUInt()
|
||||||
|
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||||
|
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> null // node is not present in the ST
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stNode!=null) {
|
||||||
|
scope.last().add(stNode)
|
||||||
|
scope.add(stNode)
|
||||||
|
}
|
||||||
|
node.children.forEach {
|
||||||
|
addToSt(it, scope)
|
||||||
|
}
|
||||||
|
if(stNode!=null)
|
||||||
|
scope.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||||
|
return value.children.map {
|
||||||
|
when(it) {
|
||||||
|
is PtAddressOf -> {
|
||||||
|
if(it.isFromArrayElement)
|
||||||
|
TODO("address-of array element $it in initial array value")
|
||||||
|
StArrayElement(null, it.identifier.name, null)
|
||||||
|
}
|
||||||
|
is PtNumber -> StArrayElement(it.number, null, null)
|
||||||
|
is PtBool -> StArrayElement(null, null, it.value)
|
||||||
|
else -> throw AssemblyError("invalid array element $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// override fun visit(decl: VarDecl) {
|
||||||
|
// val node =
|
||||||
|
// when(decl.type) {
|
||||||
|
// VarDeclType.VAR -> {
|
||||||
|
// var initialNumeric = (decl.value as? NumericLiteral)?.number
|
||||||
|
// if(initialNumeric==0.0)
|
||||||
|
// initialNumeric=null // variable will go into BSS and this will be set to 0
|
||||||
|
// val initialStringLit = decl.value as? StringLiteral
|
||||||
|
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
|
||||||
|
// val initialArrayLit = decl.value as? ArrayLiteral
|
||||||
|
// val initialArray = makeInitialArray(initialArrayLit)
|
||||||
|
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
|
||||||
|
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
|
||||||
|
// val numElements =
|
||||||
|
// if(decl.isArray)
|
||||||
|
// decl.arraysize!!.constIndex()
|
||||||
|
// else if(initialStringLit!=null)
|
||||||
|
// initialStringLit.value.length+1 // include the terminating 0-byte
|
||||||
|
// else
|
||||||
|
// null
|
||||||
|
// val bss = if(decl.datatype==DataType.STR)
|
||||||
|
// false
|
||||||
|
// else if(decl.isArray)
|
||||||
|
// initialArray.isNullOrEmpty()
|
||||||
|
// else
|
||||||
|
// initialNumeric == null
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// VarDeclType.CONST -> {
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// VarDeclType.MEMORY -> {
|
||||||
|
// val numElements =
|
||||||
|
// if(decl.isArray)
|
||||||
|
// decl.arraysize!!.constIndex()
|
||||||
|
// else null
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// scopestack.peek().add(node)
|
||||||
|
// // st.origAstLinks[decl] = node
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
|
||||||
|
// if(arrayLit==null)
|
||||||
|
// return null
|
||||||
|
// return arrayLit.value.map {
|
||||||
|
// when(it){
|
||||||
|
// is AddressOf -> {
|
||||||
|
// val scopedName = it.identifier.targetNameAndType(program).first
|
||||||
|
// StArrayElement(null, scopedName)
|
||||||
|
// }
|
||||||
|
// is IdentifierReference -> {
|
||||||
|
// val scopedName = it.targetNameAndType(program).first
|
||||||
|
// StArrayElement(null, scopedName)
|
||||||
|
// }
|
||||||
|
// is NumericLiteral -> StArrayElement(it.number, null)
|
||||||
|
// else -> throw FatalAstException("weird element dt in array literal")
|
||||||
|
// }
|
||||||
|
// }.toList()
|
||||||
|
// }
|
||||||
|
//
|
129
codeCore/src/prog8/code/ast/AstBase.kt
Normal file
129
codeCore/src/prog8/code/ast/AstBase.kt
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.IMemSizer
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.source.SourceCode
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
// New simplified AST for the code generator.
|
||||||
|
|
||||||
|
|
||||||
|
sealed class PtNode(val position: Position) {
|
||||||
|
|
||||||
|
val children = mutableListOf<PtNode>()
|
||||||
|
lateinit var parent: PtNode
|
||||||
|
|
||||||
|
fun add(child: PtNode) {
|
||||||
|
children.add(child)
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(index: Int, child: PtNode) {
|
||||||
|
children.add(index, child)
|
||||||
|
child.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||||
|
fun definingSub() = findParentNode<PtSub>(this)
|
||||||
|
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||||
|
fun definingISub() = findParentNode<IPtSubroutine>(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface IPtStatementContainer
|
||||||
|
|
||||||
|
|
||||||
|
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
|
||||||
|
|
||||||
|
|
||||||
|
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||||
|
// Note that as an exception, the 'name' is not read-only
|
||||||
|
// but a var. This is to allow for cheap node renames.
|
||||||
|
val scopedName: String
|
||||||
|
get() {
|
||||||
|
var namedParent: PtNode = this.parent
|
||||||
|
return if(namedParent is PtProgram)
|
||||||
|
name
|
||||||
|
else {
|
||||||
|
while (namedParent !is PtNamedNode)
|
||||||
|
namedParent = namedParent.parent
|
||||||
|
namedParent.scopedName + "." + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtProgram(
|
||||||
|
val name: String,
|
||||||
|
val memsizer: IMemSizer,
|
||||||
|
val encoding: IStringEncoding
|
||||||
|
) : PtNode(Position.DUMMY) {
|
||||||
|
|
||||||
|
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||||
|
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||||
|
|
||||||
|
fun allBlocks(): Sequence<PtBlock> =
|
||||||
|
children.asSequence().filterIsInstance<PtBlock>()
|
||||||
|
|
||||||
|
fun entrypoint(): PtSub? =
|
||||||
|
// returns the main.start subroutine if it exists
|
||||||
|
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
|
||||||
|
?.children
|
||||||
|
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBlock(name: String,
|
||||||
|
val library: Boolean,
|
||||||
|
val source: SourceCode, // taken from the module the block is defined in.
|
||||||
|
val options: Options,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position), IPtStatementContainer {
|
||||||
|
class Options(val address: UInt? = null,
|
||||||
|
val forceOutput: Boolean = false,
|
||||||
|
val noSymbolPrefixing: Boolean = false,
|
||||||
|
val veraFxMuls: Boolean = false,
|
||||||
|
val ignoreUnused: Boolean = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
||||||
|
init {
|
||||||
|
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
|
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
||||||
|
companion object {
|
||||||
|
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||||
|
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBreakpoint(position: Position): PtNode(position)
|
||||||
|
|
||||||
|
|
||||||
|
class PtAlign(val align: UInt, position: Position): PtNode(position)
|
||||||
|
|
||||||
|
|
||||||
|
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)
|
||||||
|
|
||||||
|
|
||||||
|
class PtNop(position: Position): PtNode(position)
|
||||||
|
|
||||||
|
|
||||||
|
// find the parent node of a specific type or interface
|
||||||
|
// (useful to figure out in what namespace/block something is defined, etc.)
|
||||||
|
inline fun <reified T> findParentNode(node: PtNode): T? {
|
||||||
|
var candidate = node.parent
|
||||||
|
while(candidate !is T && candidate !is PtProgram)
|
||||||
|
candidate = candidate.parent
|
||||||
|
return if(candidate is PtProgram)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
candidate as T
|
||||||
|
}
|
396
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
396
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.truncate
|
||||||
|
|
||||||
|
|
||||||
|
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(type.isUndefined) {
|
||||||
|
@Suppress("LeakingThis")
|
||||||
|
when(this) {
|
||||||
|
is PtBuiltinFunctionCall -> { /* void function call */ }
|
||||||
|
is PtFunctionCall -> { /* void function call */ }
|
||||||
|
is PtIdentifier -> { /* non-variable identifier */ }
|
||||||
|
else -> throw IllegalArgumentException("type should be known @$position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun isSameAs(other: PtExpression): Boolean {
|
||||||
|
return when(this) {
|
||||||
|
is PtAddressOf -> {
|
||||||
|
if(other !is PtAddressOf)
|
||||||
|
return false
|
||||||
|
if (other.type!==type || !(other.identifier isSameAs identifier))
|
||||||
|
return false
|
||||||
|
if(other.children.size!=children.size)
|
||||||
|
return false
|
||||||
|
if(children.size==1)
|
||||||
|
return true
|
||||||
|
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
||||||
|
}
|
||||||
|
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||||
|
is PtBinaryExpression -> {
|
||||||
|
if(other !is PtBinaryExpression || other.operator!=operator)
|
||||||
|
false
|
||||||
|
else if(operator in AssociativeOperators)
|
||||||
|
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
|
||||||
|
else
|
||||||
|
other.left isSameAs left && other.right isSameAs right
|
||||||
|
}
|
||||||
|
is PtContainmentCheck -> {
|
||||||
|
if(other !is PtContainmentCheck || other.type != type || !(other.needle isSameAs needle))
|
||||||
|
false
|
||||||
|
else {
|
||||||
|
if(haystackHeapVar!=null)
|
||||||
|
other.haystackHeapVar!=null && other.haystackHeapVar!! isSameAs haystackHeapVar!!
|
||||||
|
else if(haystackValues!=null)
|
||||||
|
other.haystackValues!=null && other.haystackValues!! isSameAs haystackValues!!
|
||||||
|
else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||||
|
is PtIrRegister -> other is PtIrRegister && other.type==type && other.register==register
|
||||||
|
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||||
|
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
||||||
|
is PtBool -> other is PtBool && other.value==value
|
||||||
|
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
|
||||||
|
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
|
||||||
|
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
|
||||||
|
target.memory != null && this is PtMemoryByte -> {
|
||||||
|
target.memory!!.address isSameAs this.address
|
||||||
|
}
|
||||||
|
target.identifier != null && this is PtIdentifier -> {
|
||||||
|
this.name == target.identifier!!.name
|
||||||
|
}
|
||||||
|
target.array != null && this is PtArrayIndexer -> {
|
||||||
|
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
|
||||||
|
fun asConstValue(): Double? = (this as? PtNumber)?.number ?: (this as? PtBool)?.asInt()?.toDouble()
|
||||||
|
|
||||||
|
fun isSimple(): Boolean {
|
||||||
|
return when(this) {
|
||||||
|
is PtAddressOf -> this.arrayIndexExpr==null || this.arrayIndexExpr?.isSimple()==true
|
||||||
|
is PtArray -> true
|
||||||
|
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||||
|
is PtBinaryExpression -> false
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
when (name) {
|
||||||
|
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtContainmentCheck -> false
|
||||||
|
is PtFunctionCall -> false
|
||||||
|
is PtIdentifier -> true
|
||||||
|
is PtIrRegister -> true
|
||||||
|
is PtMemoryByte -> address is PtNumber
|
||||||
|
is PtBool -> true
|
||||||
|
is PtNumber -> true
|
||||||
|
is PtPrefix -> value.isSimple()
|
||||||
|
is PtRange -> true
|
||||||
|
is PtString -> true
|
||||||
|
is PtTypeCast -> value.isSimple()
|
||||||
|
is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fun clone(): PtExpression {
|
||||||
|
fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression {
|
||||||
|
orig.children.forEach { clone.add((it as PtExpression).clone()) }
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
when(this) {
|
||||||
|
is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position))
|
||||||
|
is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position))
|
||||||
|
is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position))
|
||||||
|
is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position))
|
||||||
|
is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position))
|
||||||
|
is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position))
|
||||||
|
is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position))
|
||||||
|
is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position))
|
||||||
|
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||||
|
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||||
|
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||||
|
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
|
||||||
|
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||||
|
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||||
|
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||||
|
is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
class PtAddressOf(position: Position, val isMsbForSplitArray: Boolean=false) : PtExpression(DataType.forDt(BaseDataType.UWORD), position) {
|
||||||
|
val identifier: PtIdentifier
|
||||||
|
get() = children[0] as PtIdentifier
|
||||||
|
val arrayIndexExpr: PtExpression?
|
||||||
|
get() = if(children.size==2) children[1] as PtExpression else null
|
||||||
|
|
||||||
|
val isFromArrayElement: Boolean
|
||||||
|
get() = children.size==2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
|
||||||
|
val variable: PtIdentifier
|
||||||
|
get() = children[0] as PtIdentifier
|
||||||
|
val index: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
val splitWords: Boolean
|
||||||
|
get() = variable.type.isSplitWordArray
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(elementType.isNumericOrBool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
// children are always one of 3 types: PtBool, PtNumber or PtAddressOf.
|
||||||
|
override fun hashCode(): Int = Objects.hash(children, type)
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtArray)
|
||||||
|
return false
|
||||||
|
return type==other.type && children == other.children
|
||||||
|
}
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
get() = children.size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBuiltinFunctionCall(val name: String,
|
||||||
|
val void: Boolean,
|
||||||
|
val hasNoSideEffects: Boolean,
|
||||||
|
type: DataType,
|
||||||
|
position: Position) : PtExpression(type, position) {
|
||||||
|
init {
|
||||||
|
if(!void)
|
||||||
|
require(!type.isUndefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
val args: List<PtExpression>
|
||||||
|
get() = children.map { it as PtExpression }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
val left: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val right: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(operator in ComparisonOperators + LogicalOperators)
|
||||||
|
require(type.isBool)
|
||||||
|
else
|
||||||
|
require(!type.isBool) { "no bool allowed for this operator $operator"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtIfExpression(type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
val condition: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val truevalue: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
val falsevalue: PtExpression
|
||||||
|
get() = children[2] as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtContainmentCheck(position: Position): PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
|
||||||
|
val needle: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val haystackHeapVar: PtIdentifier?
|
||||||
|
get() = children[1] as? PtIdentifier
|
||||||
|
val haystackValues: PtArray?
|
||||||
|
get() = children[1] as? PtArray
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
|
||||||
|
const val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtFunctionCall(val name: String,
|
||||||
|
val void: Boolean,
|
||||||
|
type: DataType,
|
||||||
|
position: Position) : PtExpression(type, position) {
|
||||||
|
val args: List<PtExpression>
|
||||||
|
get() = children.map { it as PtExpression }
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(void) require(type.isUndefined) {
|
||||||
|
"void fcall should have undefined datatype"
|
||||||
|
}
|
||||||
|
// note: non-void calls can have UNDEFINED type: if they return more than 1 value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "[PtIdentifier:$name $type $position]"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copy() = PtIdentifier(name, type, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtMemoryByte(position: Position) : PtExpression(DataType.forDt(BaseDataType.UBYTE), position) {
|
||||||
|
val address: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
|
||||||
|
override fun hashCode(): Int = Objects.hash(type, value)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtBool)
|
||||||
|
return false
|
||||||
|
return value==other.value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "PtBool:$value"
|
||||||
|
|
||||||
|
fun asInt(): Int = if(value) 1 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtExpression(DataType.forDt(type), position) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
|
||||||
|
PtNumber(BaseDataType.UBYTE, if(bool) 1.0 else 0.0, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(type==BaseDataType.BOOL)
|
||||||
|
throw IllegalArgumentException("use PtBool instead")
|
||||||
|
if(type!=BaseDataType.FLOAT) {
|
||||||
|
val trunc = truncate(number)
|
||||||
|
if (trunc != number)
|
||||||
|
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
|
||||||
|
}
|
||||||
|
when(type) {
|
||||||
|
BaseDataType.UBYTE -> require(number in 0.0..255.0)
|
||||||
|
BaseDataType.BYTE -> require(number in -128.0..127.0)
|
||||||
|
BaseDataType.UWORD -> require(number in 0.0..65535.0)
|
||||||
|
BaseDataType.WORD -> require(number in -32728.0..32767.0)
|
||||||
|
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
|
||||||
|
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = Objects.hash(type, number)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return if(other==null || other !is PtNumber)
|
||||||
|
false
|
||||||
|
else if(!type.isBool && !other.type.isBool)
|
||||||
|
number==other.number
|
||||||
|
else
|
||||||
|
type==other.type && number==other.number
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||||
|
|
||||||
|
override fun toString() = "PtNumber:$type:$number"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
|
val value: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(operator in PrefixOperators) { "invalid prefix operator: $operator" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtRange(type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
val from: PtExpression
|
||||||
|
get() = children[0] as PtExpression
|
||||||
|
val to: PtExpression
|
||||||
|
get() = children[1] as PtExpression
|
||||||
|
val step: PtNumber
|
||||||
|
get() = children[2] as PtNumber
|
||||||
|
|
||||||
|
fun toConstantIntegerRange(): IntProgression? {
|
||||||
|
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||||
|
return when {
|
||||||
|
fromVal == toVal -> fromVal .. toVal
|
||||||
|
fromVal <= toVal -> when {
|
||||||
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == 1 -> fromVal..toVal
|
||||||
|
else -> fromVal..toVal step stepVal
|
||||||
|
}
|
||||||
|
else -> when {
|
||||||
|
stepVal >= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == -1 -> fromVal downTo toVal
|
||||||
|
else -> fromVal downTo toVal step abs(stepVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val fromLv = from as? PtNumber
|
||||||
|
val toLv = to as? PtNumber
|
||||||
|
if(fromLv==null || toLv==null)
|
||||||
|
return null
|
||||||
|
val fromVal = fromLv.number.toInt()
|
||||||
|
val toVal = toLv.number.toInt()
|
||||||
|
val stepVal = step.number.toInt()
|
||||||
|
return makeRange(fromVal, toVal, stepVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.forDt(BaseDataType.STR), position) {
|
||||||
|
override fun hashCode(): Int = Objects.hash(value, encoding)
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is PtString)
|
||||||
|
return false
|
||||||
|
return value==other.value && encoding == other.encoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
|
||||||
|
val value: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
|
||||||
|
fun copy(): PtTypeCast {
|
||||||
|
val copy = PtTypeCast(type.base, position)
|
||||||
|
if(children[0] is PtIdentifier) {
|
||||||
|
copy.add((children[0] as PtIdentifier).copy())
|
||||||
|
} else {
|
||||||
|
TODO("cannot copy node ${children[0]}")
|
||||||
|
}
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
||||||
|
class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
209
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
209
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.escape
|
||||||
|
import prog8.code.core.toHex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
||||||
|
* passing it as a String to the specified receiver function.
|
||||||
|
*/
|
||||||
|
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
|
||||||
|
fun type(dt: DataType) = "!${dt}!"
|
||||||
|
fun txt(node: PtNode): String {
|
||||||
|
return when(node) {
|
||||||
|
is PtAlign -> "%align ${node.align}"
|
||||||
|
is PtAssignTarget -> if(node.void) "<void>" else "<target>"
|
||||||
|
is PtAssignment -> "<assign>"
|
||||||
|
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
|
||||||
|
is PtBreakpoint -> "%breakpoint"
|
||||||
|
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||||
|
is PtAddressOf -> {
|
||||||
|
if(node.isMsbForSplitArray)
|
||||||
|
"&>"
|
||||||
|
else if(node.isFromArrayElement)
|
||||||
|
"& array-element"
|
||||||
|
else
|
||||||
|
"&"
|
||||||
|
}
|
||||||
|
is PtArray -> {
|
||||||
|
val valuelist = node.children.joinToString(", ") {
|
||||||
|
when (it) {
|
||||||
|
is PtBool -> it.toString()
|
||||||
|
is PtNumber -> it.number.toString()
|
||||||
|
is PtIdentifier -> it.name
|
||||||
|
is PtAddressOf -> "& ${it.identifier.name}"
|
||||||
|
else -> "invalid array element $it"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"array len=${node.children.size} ${type(node.type)} [ $valuelist ]"
|
||||||
|
}
|
||||||
|
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||||
|
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
val str = if(node.void) "void " else ""
|
||||||
|
str + node.name + "()"
|
||||||
|
}
|
||||||
|
is PtContainmentCheck -> "in"
|
||||||
|
is PtFunctionCall -> {
|
||||||
|
val str = if(node.void) "void " else ""
|
||||||
|
str + node.name + "()"
|
||||||
|
}
|
||||||
|
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||||
|
is PtIrRegister -> "IRREG#${node.register} ${type(node.type)}"
|
||||||
|
is PtMemoryByte -> "@()"
|
||||||
|
is PtNumber -> {
|
||||||
|
val numstr = if(node.type.isFloat) node.number.toString() else node.number.toHex()
|
||||||
|
"$numstr ${type(node.type)}"
|
||||||
|
}
|
||||||
|
is PtBool -> node.value.toString()
|
||||||
|
is PtPrefix -> node.operator
|
||||||
|
is PtRange -> "<range>"
|
||||||
|
is PtString -> "\"${node.value.escape()}\""
|
||||||
|
is PtTypeCast -> "as ${node.type}"
|
||||||
|
is PtForLoop -> "for"
|
||||||
|
is PtIfElse -> "ifelse"
|
||||||
|
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
|
||||||
|
is PtInlineAssembly -> {
|
||||||
|
if(node.isIR)
|
||||||
|
"%ir {{ ...${node.assembly.length} characters... }}"
|
||||||
|
else
|
||||||
|
"%asm {{ ...${node.assembly.length} characters... }}"
|
||||||
|
}
|
||||||
|
is PtJump -> {
|
||||||
|
"goto ${txt(node.target)}"
|
||||||
|
}
|
||||||
|
is PtAsmSub -> {
|
||||||
|
val params = node.parameters.joinToString(", ") {
|
||||||
|
val register = it.first.registerOrPair
|
||||||
|
val statusflag = it.first.statusflag
|
||||||
|
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
|
||||||
|
}
|
||||||
|
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||||
|
val returns = if (node.returns.isEmpty()) "" else {
|
||||||
|
"-> ${node.returns.joinToString(", ") {
|
||||||
|
val register = it.first.registerOrPair
|
||||||
|
val statusflag = it.first.statusflag
|
||||||
|
"${it.second} @${register ?: statusflag}"
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
}
|
||||||
|
val str = if (node.inline) "inline " else ""
|
||||||
|
if(node.address == null) {
|
||||||
|
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||||
|
} else {
|
||||||
|
val bank = if(node.address.constbank!=null) "@bank ${node.address.constbank}"
|
||||||
|
else if(node.address.varbank!=null) "@bank ${node.address.varbank?.name}"
|
||||||
|
else ""
|
||||||
|
str + "extsub $bank ${node.address.address.toHex()} = ${node.name}($params) $clobbers $returns"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtBlock -> {
|
||||||
|
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}"
|
||||||
|
"\nblock '${node.name}' $addr"
|
||||||
|
}
|
||||||
|
is PtConstant -> {
|
||||||
|
val value = when {
|
||||||
|
node.type.isBool -> if(node.value==0.0) "false" else "true"
|
||||||
|
node.type.isInteger -> node.value.toInt().toString()
|
||||||
|
else -> node.value.toString()
|
||||||
|
}
|
||||||
|
"const ${node.type} ${node.name} = $value"
|
||||||
|
}
|
||||||
|
is PtLabel -> "${node.name}:"
|
||||||
|
is PtMemMapped -> {
|
||||||
|
if(node.type.isArray) {
|
||||||
|
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
|
||||||
|
"&${node.type.elementType()}[$arraysize] ${node.name} = ${node.address.toHex()}"
|
||||||
|
} else {
|
||||||
|
"&${node.type} ${node.name} = ${node.address.toHex()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtSub -> {
|
||||||
|
val params = node.parameters.joinToString(", ") {
|
||||||
|
val reg = if(it.register!=null) "@${it.register}" else ""
|
||||||
|
"${it.type} ${it.name} $reg"
|
||||||
|
}
|
||||||
|
var str = "sub ${node.name}($params) "
|
||||||
|
if(node.returns.isNotEmpty())
|
||||||
|
str += "-> ${node.returns.joinToString(",")}"
|
||||||
|
str
|
||||||
|
}
|
||||||
|
is PtVariable -> {
|
||||||
|
val split = if(node.type.isSplitWordArray) "" else "@nosplit"
|
||||||
|
val align = when(node.align) {
|
||||||
|
0u -> ""
|
||||||
|
2u -> "@alignword"
|
||||||
|
64u -> "@align64"
|
||||||
|
256u -> "@alignpage"
|
||||||
|
else -> throw IllegalArgumentException("invalid alignment size")
|
||||||
|
}
|
||||||
|
val str = if(node.arraySize!=null) {
|
||||||
|
val eltType = node.type.elementType()
|
||||||
|
"${eltType}[${node.arraySize}] $split $align ${node.name}"
|
||||||
|
}
|
||||||
|
else if(node.type.isArray) {
|
||||||
|
val eltType = node.type.elementType()
|
||||||
|
"${eltType}[] $split $align ${node.name}"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
"${node.type} ${node.name}"
|
||||||
|
if(node.value!=null)
|
||||||
|
str + " = " + txt(node.value)
|
||||||
|
else
|
||||||
|
str
|
||||||
|
}
|
||||||
|
is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else ""
|
||||||
|
is PtNop -> "nop"
|
||||||
|
is PtProgram -> "PROGRAM ${node.name}"
|
||||||
|
is PtRepeatLoop -> "repeat"
|
||||||
|
is PtReturn -> "return"
|
||||||
|
is PtSubroutineParameter -> {
|
||||||
|
val reg = if(node.register!=null) "@${node.register}" else ""
|
||||||
|
"${node.type} ${node.name} $reg"
|
||||||
|
}
|
||||||
|
is PtWhen -> "when"
|
||||||
|
is PtWhenChoice -> {
|
||||||
|
if(node.isElse)
|
||||||
|
"else"
|
||||||
|
else
|
||||||
|
"->"
|
||||||
|
}
|
||||||
|
is PtDefer -> "<defer>"
|
||||||
|
is PtIfExpression -> "<ifexpr>"
|
||||||
|
is PtJmpTable -> "<jmptable>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(root is PtProgram) {
|
||||||
|
output(txt(root))
|
||||||
|
root.children.forEach {
|
||||||
|
walkAst(it) { node, depth ->
|
||||||
|
val txt = txt(node)
|
||||||
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
|
if(!library || !skipLibraries) {
|
||||||
|
if (txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
} else {
|
||||||
|
walkAst(root) { node, depth ->
|
||||||
|
val txt = txt(node)
|
||||||
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
|
if(!library || !skipLibraries) {
|
||||||
|
if (txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
|
||||||
|
fun recurse(node: PtNode, depth: Int) {
|
||||||
|
act(node, depth)
|
||||||
|
node.children.forEach { recurse(it, depth+1) }
|
||||||
|
}
|
||||||
|
recurse(root, 0)
|
||||||
|
}
|
198
codeCore/src/prog8/code/ast/AstStatements.kt
Normal file
198
codeCore/src/prog8/code/ast/AstStatements.kt
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface IPtSubroutine {
|
||||||
|
val name: String
|
||||||
|
val scopedName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class PtAsmSub(
|
||||||
|
name: String,
|
||||||
|
val address: Address?,
|
||||||
|
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 Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtSub(
|
||||||
|
name: String,
|
||||||
|
val parameters: List<PtSubroutineParameter>,
|
||||||
|
val returns: List<DataType>,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||||
|
init {
|
||||||
|
// params and return values should not be str
|
||||||
|
if(parameters.any{ !it.type.isNumericOrBool })
|
||||||
|
throw AssemblyError("non-numeric/non-bool parameter")
|
||||||
|
if(returns.any { !it.isNumericOrBool })
|
||||||
|
throw AssemblyError("non-numeric/non-bool returntype")
|
||||||
|
parameters.forEach { it.parent=this }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtSubroutineParameter(name: String, val type: DataType, val register: RegisterOrPair?, 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, val isVarInitializer: Boolean=false) : 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(position: Position) : PtNode(position) {
|
||||||
|
val target: PtExpression
|
||||||
|
get() = children.single() as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) // children are all expressions
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface IPtVariable {
|
||||||
|
val name: String
|
||||||
|
val type: DataType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtVariable(
|
||||||
|
name: String,
|
||||||
|
override val type: DataType,
|
||||||
|
val zeropage: ZeropageWish,
|
||||||
|
val align: UInt,
|
||||||
|
val value: PtExpression?,
|
||||||
|
val arraySize: UInt?,
|
||||||
|
position: Position
|
||||||
|
) : PtNamedNode(name, position), IPtVariable {
|
||||||
|
init {
|
||||||
|
|
||||||
|
if(value!=null) {
|
||||||
|
require(value is PtArray || value is PtString) { "variable initializer value must only be array or string" }
|
||||||
|
// NOTE: the 6502 code generator expects numerical variables to not have an initialization value,
|
||||||
|
// because that is done via assignment statements. There are no "inline" variables with a given value.
|
||||||
|
// All variables are put into zeropage or into the BSS section and initialized afterwards during program
|
||||||
|
// startup or at the start of the subroutine.
|
||||||
|
// The IR codegen however is different it has a special section <VARIABLESWITHINIT> for all variables
|
||||||
|
// that have a non-zero initialization value, regardless of the datatype. It removes the initialization
|
||||||
|
// assignment and puts the value back into the variable (but only in the symboltable).
|
||||||
|
}
|
||||||
|
|
||||||
|
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.isString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtDefer(position: Position): PtNode(position), IPtStatementContainer
|
||||||
|
|
||||||
|
|
||||||
|
class PtJmpTable(position: Position) : PtNode(position) // contains only PtIdentifier nodes
|
16
codeCore/src/prog8/code/ast/Verify.kt
Normal file
16
codeCore/src/prog8/code/ast/Verify.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package prog8.code.ast
|
||||||
|
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
fun verifyFinalAstBeforeAsmGen(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||||
|
/*
|
||||||
|
walkAst(program) { node, _ ->
|
||||||
|
if(node is PtVariable) {
|
||||||
|
if(node.value!=null) {
|
||||||
|
// require(node.type in ArrayDatatypes || node.type==DataType.STR) { "final check before asmgen: only string and array variables can still have an init value ${node.name} ${node.type} ${node.position}"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
152
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
152
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
|
||||||
|
class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
|
||||||
|
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
||||||
|
override fun toString(): String {
|
||||||
|
val paramConvs = params.mapIndexed { index, it ->
|
||||||
|
when {
|
||||||
|
it.reg!=null -> "$index:${it.reg}"
|
||||||
|
it.variable -> "$index:variable"
|
||||||
|
else -> "$index:???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val returnConv =
|
||||||
|
when {
|
||||||
|
returns.reg == RegisterOrPair.FAC1 -> "floatFAC1"
|
||||||
|
returns.reg != null -> returns.reg.toString()
|
||||||
|
else -> "<no returnvalue>"
|
||||||
|
}
|
||||||
|
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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?
|
||||||
|
val returnType: BaseDataType?,
|
||||||
|
vararg val parameters: FParam) {
|
||||||
|
|
||||||
|
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
|
||||||
|
val returns: ReturnConvention = when (returnType) {
|
||||||
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||||
|
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||||
|
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
||||||
|
null -> ReturnConvention(null, null)
|
||||||
|
else -> {
|
||||||
|
// return type depends on arg type
|
||||||
|
when (val paramType = actualParamTypes.first()) {
|
||||||
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||||
|
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||||
|
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||||
|
else -> ReturnConvention(paramType, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||||
|
actualParamTypes.size==1 -> {
|
||||||
|
// 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]) {
|
||||||
|
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD -> 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 IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
else -> ParamConvention(paramType, null, false)
|
||||||
|
}
|
||||||
|
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 -> {
|
||||||
|
// multiple parameters go via variables
|
||||||
|
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||||
|
CallConvention(paramConvs, returns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||||
|
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
|
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
|
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
|
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
|
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
|
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||||
|
"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
|
||||||
|
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||||
|
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||||
|
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||||
|
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||||
|
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||||
|
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||||
|
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||||
|
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||||
|
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||||
|
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||||
|
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||||
|
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||||
|
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
|
||||||
|
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||||
|
"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)),
|
||||||
|
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
|
||||||
|
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
|
||||||
|
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
|
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
|
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
|
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||||
|
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||||
|
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||||
|
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||||
|
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||||
|
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||||
|
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||||
|
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
|
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
|
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||||
|
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||||
|
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||||
|
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
|
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||||
|
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||||
|
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||||
|
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||||
|
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||||
|
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||||
|
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||||
|
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||||
|
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||||
|
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||||
|
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||||
|
"rsave" to FSignature(false, null),
|
||||||
|
"rrestore" to FSignature(false, null),
|
||||||
|
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
|
||||||
|
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
|
||||||
|
"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)),
|
||||||
|
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||||
|
)
|
||||||
|
|
||||||
|
val InplaceModifyingBuiltinFunctions = setOf(
|
||||||
|
"setlsb", "setmsb",
|
||||||
|
"rol", "ror", "rol2", "ror2",
|
||||||
|
"divmod", "divmod__ubyte", "divmod__uword"
|
||||||
|
)
|
43
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
43
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
|
||||||
|
class CompilationOptions(val output: OutputType,
|
||||||
|
val launcher: CbmPrgLauncherType,
|
||||||
|
val zeropage: ZeropageType,
|
||||||
|
val zpReserved: List<UIntRange>,
|
||||||
|
val zpAllowed: List<UIntRange>,
|
||||||
|
val floats: Boolean,
|
||||||
|
val noSysInit: Boolean,
|
||||||
|
val compTarget: ICompilationTarget,
|
||||||
|
// these are set later, based on command line arguments or options in the source code:
|
||||||
|
var loadAddress: UInt,
|
||||||
|
var memtopAddress: UInt,
|
||||||
|
var warnSymbolShadowing: Boolean = false,
|
||||||
|
var optimize: Boolean = false,
|
||||||
|
var asmQuiet: Boolean = false,
|
||||||
|
var asmListfile: Boolean = false,
|
||||||
|
var includeSourcelines: Boolean = false,
|
||||||
|
var dumpVariables: Boolean = false,
|
||||||
|
var dumpSymbols: Boolean = false,
|
||||||
|
var experimentalCodegen: Boolean = false,
|
||||||
|
var varsHighBank: Int? = null,
|
||||||
|
var varsGolden: Boolean = false,
|
||||||
|
var slabsHighBank: Int? = null,
|
||||||
|
var slabsGolden: Boolean = false,
|
||||||
|
var dontSplitWordArrays: Boolean = false,
|
||||||
|
var breakpointCpuInstruction: String? = null,
|
||||||
|
var ignoreFootguns: Boolean = false,
|
||||||
|
var outputDir: Path = Path(""),
|
||||||
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
compTarget.initializeMemoryAreas(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
|
||||||
|
}
|
||||||
|
}
|
97
codeCore/src/prog8/code/core/Conversions.kt
Normal file
97
codeCore/src/prog8/code/core/Conversions.kt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
val powersOfTwoFloat = (0..16).map { (2.0).pow(it) }.toTypedArray()
|
||||||
|
val negativePowersOfTwoFloat = powersOfTwoFloat.map { -it }.toTypedArray()
|
||||||
|
val powersOfTwoInt = (0..16).map { 2.0.pow(it).toInt() }.toTypedArray()
|
||||||
|
|
||||||
|
fun Number.toHex(): String {
|
||||||
|
// 0..15 -> "0".."15"
|
||||||
|
// 16..255 -> "$10".."$ff"
|
||||||
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
// larger -> "$12345678"
|
||||||
|
// negative values are prefixed with '-'.
|
||||||
|
val integer = this.toInt()
|
||||||
|
if(integer<0)
|
||||||
|
return '-' + abs(integer).toHex()
|
||||||
|
return when (integer) {
|
||||||
|
in 0 until 16 -> integer.toString()
|
||||||
|
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||||
|
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
||||||
|
else -> "$"+integer.toString(16).padStart(8,'0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UInt.toHex(): String {
|
||||||
|
// 0..15 -> "0".."15"
|
||||||
|
// 16..255 -> "$10".."$ff"
|
||||||
|
// 256..65536 -> "$0100".."$ffff"
|
||||||
|
// larger -> "$12345678"
|
||||||
|
return when (this) {
|
||||||
|
in 0u until 16u -> this.toString()
|
||||||
|
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
|
||||||
|
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
|
||||||
|
else -> "$"+this.toString(16).padStart(8,'0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Char.escape(): Char = this.toString().escape()[0]
|
||||||
|
|
||||||
|
fun String.escape(): String {
|
||||||
|
val es = this.map {
|
||||||
|
when(it) {
|
||||||
|
'\t' -> "\\t"
|
||||||
|
'\n' -> "\\n"
|
||||||
|
'\r' -> "\\r"
|
||||||
|
'"' -> "\\\""
|
||||||
|
in '\u8000'..'\u80ff' -> "\\x" + (it.code - 0x8000).toString(16).padStart(2, '0') // 'ugly' passthrough hack
|
||||||
|
in '\u0000'..'\u00ff' -> it.toString()
|
||||||
|
else -> "\\u" + it.code.toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return es.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.unescape(): String {
|
||||||
|
val result = mutableListOf<Char>()
|
||||||
|
val iter = this.iterator()
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
val c = iter.nextChar()
|
||||||
|
if(c=='\\') {
|
||||||
|
val ec = iter.nextChar()
|
||||||
|
result.add(when(ec) {
|
||||||
|
'\\' -> '\\'
|
||||||
|
'n' -> '\n'
|
||||||
|
'r' -> '\r'
|
||||||
|
't' -> '\t'
|
||||||
|
'"' -> '"'
|
||||||
|
'\'' -> '\''
|
||||||
|
'u' -> {
|
||||||
|
try {
|
||||||
|
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||||
|
} catch (sb: StringIndexOutOfBoundsException) {
|
||||||
|
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||||
|
} catch (nf: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'x' -> {
|
||||||
|
try {
|
||||||
|
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||||
|
(0x8000 + hex).toChar() // 'ugly' pass-through hack
|
||||||
|
} catch (sb: StringIndexOutOfBoundsException) {
|
||||||
|
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||||
|
} catch (nf: NumberFormatException) {
|
||||||
|
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("invalid escape char in string: \\$ec")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.add(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.joinToString("")
|
||||||
|
}
|
348
codeCore/src/prog8/code/core/Enumerations.kt
Normal file
348
codeCore/src/prog8/code/core/Enumerations.kt
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.util.Objects
|
||||||
|
|
||||||
|
enum class BaseDataType {
|
||||||
|
UBYTE, // pass by value 8 bits unsigned
|
||||||
|
BYTE, // pass by value 8 bits signed
|
||||||
|
UWORD, // pass by value 16 bits unsigned
|
||||||
|
WORD, // pass by value 16 bits signed
|
||||||
|
LONG, // pass by value 32 bits signed
|
||||||
|
FLOAT, // pass by value machine dependent
|
||||||
|
BOOL, // pass by value bit 0 of an 8-bit byte
|
||||||
|
STR, // pass by reference
|
||||||
|
ARRAY, // pass by reference, subtype is the element type
|
||||||
|
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||||
|
UNDEFINED;
|
||||||
|
|
||||||
|
|
||||||
|
fun largerSizeThan(other: BaseDataType) =
|
||||||
|
when {
|
||||||
|
this == other -> false
|
||||||
|
this.isByteOrBool -> false
|
||||||
|
this.isWord -> other.isByteOrBool
|
||||||
|
this == LONG -> other.isByteOrBool || other.isWord
|
||||||
|
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||||
|
this.isArray && other.isArray -> false
|
||||||
|
this.isArray -> other != FLOAT
|
||||||
|
this == STR -> other != FLOAT
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun equalsSize(other: BaseDataType) =
|
||||||
|
when {
|
||||||
|
this == other -> true
|
||||||
|
this.isArray && other.isArray -> true
|
||||||
|
this.isByteOrBool -> other.isByteOrBool
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
A,
|
||||||
|
X,
|
||||||
|
Y
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RegisterOrPair {
|
||||||
|
A,
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
AX,
|
||||||
|
AY,
|
||||||
|
XY,
|
||||||
|
FAC1,
|
||||||
|
FAC2,
|
||||||
|
// cx16 virtual registers:
|
||||||
|
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||||
|
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { entries.map { it.toString()} }
|
||||||
|
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
|
||||||
|
return when(cpu) {
|
||||||
|
CpuRegister.A -> A
|
||||||
|
CpuRegister.X -> X
|
||||||
|
CpuRegister.Y -> Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asCpuRegister(): CpuRegister = when(this) {
|
||||||
|
A -> CpuRegister.A
|
||||||
|
X -> CpuRegister.X
|
||||||
|
Y -> CpuRegister.Y
|
||||||
|
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
|
||||||
|
|
||||||
|
enum class Statusflag {
|
||||||
|
Pc,
|
||||||
|
Pz, // don't use
|
||||||
|
Pv,
|
||||||
|
Pn; // don't use
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { entries.map { it.toString()} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BranchCondition {
|
||||||
|
CS,
|
||||||
|
CC,
|
||||||
|
EQ, // EQ == Z
|
||||||
|
Z,
|
||||||
|
NE, // NE == NZ
|
||||||
|
NZ,
|
||||||
|
MI, // MI == NEG
|
||||||
|
NEG,
|
||||||
|
PL, // PL == POS
|
||||||
|
POS,
|
||||||
|
VS,
|
||||||
|
VC
|
||||||
|
}
|
||||||
|
|
||||||
|
val Cx16VirtualRegisters = arrayOf(
|
||||||
|
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||||
|
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
||||||
|
RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11,
|
||||||
|
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||||
|
)
|
||||||
|
|
||||||
|
val CpuRegisters = arrayOf(
|
||||||
|
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||||
|
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
enum class OutputType {
|
||||||
|
RAW,
|
||||||
|
PRG,
|
||||||
|
XEX,
|
||||||
|
LIBRARY
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CbmPrgLauncherType {
|
||||||
|
BASIC,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ZeropageType {
|
||||||
|
BASICSAFE,
|
||||||
|
FLOATSAFE,
|
||||||
|
KERNALSAFE,
|
||||||
|
FULL,
|
||||||
|
DONTUSE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ZeropageWish {
|
||||||
|
REQUIRE_ZEROPAGE,
|
||||||
|
PREFER_ZEROPAGE,
|
||||||
|
DONTCARE,
|
||||||
|
NOT_IN_ZEROPAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SplitWish {
|
||||||
|
DONTCARE,
|
||||||
|
SPLIT,
|
||||||
|
NOSPLIT
|
||||||
|
}
|
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
class InternalCompilerException(message: String?) : Exception(message)
|
||||||
|
|
||||||
|
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||||
|
|
||||||
|
class ErrorsReportedException(message: String?) : Exception(message)
|
17
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
17
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
|
||||||
|
interface ICodeGeneratorBackend {
|
||||||
|
fun generate(program: PtProgram,
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
options: CompilationOptions,
|
||||||
|
errors: IErrorReporter): IAssemblyProgram?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface IAssemblyProgram {
|
||||||
|
val name: String
|
||||||
|
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
|
||||||
|
}
|
43
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
43
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
enum class CpuType {
|
||||||
|
CPU6502,
|
||||||
|
CPU65C02,
|
||||||
|
VIRTUAL
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
|
}
|
16
codeCore/src/prog8/code/core/IErrorReporter.kt
Normal file
16
codeCore/src/prog8/code/core/IErrorReporter.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
interface IErrorReporter {
|
||||||
|
fun err(msg: String, position: Position)
|
||||||
|
fun warn(msg: String, position: Position)
|
||||||
|
fun info(msg: String, position: Position)
|
||||||
|
fun undefined(symbol: List<String>, position: Position)
|
||||||
|
fun noErrors(): Boolean
|
||||||
|
fun report()
|
||||||
|
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||||
|
if(numErrors>0)
|
||||||
|
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun noErrorForLine(position: Position): Boolean
|
||||||
|
}
|
15
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
15
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
interface IMemSizer {
|
||||||
|
fun memorySize(dt: DataType, numElements: Int?): Int
|
||||||
|
|
||||||
|
fun memorySize(dt: BaseDataType): Int {
|
||||||
|
if(dt.isPassByRef)
|
||||||
|
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
||||||
|
try {
|
||||||
|
return memorySize(DataType.forDt(dt), null)
|
||||||
|
} catch (x: NoSuchElementException) {
|
||||||
|
throw IllegalArgumentException(x.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
21
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
enum class Encoding(val prefix: String) {
|
||||||
|
DEFAULT("default"), // depends on compilation target
|
||||||
|
PETSCII("petscii"), // c64/c128/cx16
|
||||||
|
SCREENCODES("sc"), // c64/c128/cx16
|
||||||
|
ATASCII("atascii"), // atari
|
||||||
|
ISO("iso"), // cx16 (iso-8859-15)
|
||||||
|
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||||
|
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||||
|
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
||||||
|
KATAKANA("kata"), // cx16 (katakana)
|
||||||
|
C64OS("c64os") // c64 (C64 OS)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStringEncoding {
|
||||||
|
val defaultEncoding: Encoding
|
||||||
|
|
||||||
|
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
|
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
|
}
|
170
codeCore/src/prog8/code/core/MemoryRegions.kt
Normal file
170
codeCore/src/prog8/code/core/MemoryRegions.kt
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
|
||||||
|
|
||||||
|
class MemAllocationError(message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
|
abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
||||||
|
data class VarAllocation(val address: UInt, val dt: DataType, val size: Int)
|
||||||
|
|
||||||
|
abstract fun allocate(name: String,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||||
|
|
||||||
|
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||||
|
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||||
|
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||||
|
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
|
|
||||||
|
// the variables allocated into Zeropage.
|
||||||
|
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||||
|
val allocatedVariables = mutableMapOf<String, VarAllocation>()
|
||||||
|
|
||||||
|
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
|
fun removeReservedFromFreePool() {
|
||||||
|
synchronized(this) {
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
|
||||||
|
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retainAllowed() {
|
||||||
|
synchronized(this) {
|
||||||
|
for(allowed in options.zpAllowed)
|
||||||
|
free.retainAll { it in allowed }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
fun hasByteAvailable() = if(options.zeropage== ZeropageType.DONTUSE) false else free.isNotEmpty()
|
||||||
|
fun hasWordAvailable(): Boolean {
|
||||||
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return free.windowed(2).any { it[0] == it[1] - 1u }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun allocate(name: String,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||||
|
|
||||||
|
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
||||||
|
|
||||||
|
if(options.zeropage== ZeropageType.DONTUSE)
|
||||||
|
return Err(MemAllocationError("zero page usage has been disabled"))
|
||||||
|
|
||||||
|
val size: Int =
|
||||||
|
when {
|
||||||
|
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||||
|
datatype.isString || datatype.isArray -> {
|
||||||
|
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||||
|
if(position!=null)
|
||||||
|
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
|
||||||
|
else
|
||||||
|
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
|
||||||
|
memsize
|
||||||
|
}
|
||||||
|
datatype.isFloat -> {
|
||||||
|
if (options.floats) {
|
||||||
|
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||||
|
if(position!=null)
|
||||||
|
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||||
|
else
|
||||||
|
errors.warn("$name: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
|
||||||
|
memsize
|
||||||
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
|
}
|
||||||
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
if(free.isNotEmpty()) {
|
||||||
|
if(size==1) {
|
||||||
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
|
if(oneSeparateByteFree(candidate))
|
||||||
|
return Ok(VarAllocation(makeAllocation(candidate, 1, datatype, name), datatype,1))
|
||||||
|
}
|
||||||
|
return Ok(VarAllocation(makeAllocation(free[0], 1, datatype, name), datatype,1))
|
||||||
|
}
|
||||||
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||||
|
if (sequentialFree(candidate, size))
|
||||||
|
return Ok(VarAllocation(makeAllocation(candidate, size, datatype, name), datatype, size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(MemAllocationError("no more free space in ZP to allocate $size sequential bytes"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reserve(range: UIntRange) = free.removeAll(range)
|
||||||
|
|
||||||
|
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: String): UInt {
|
||||||
|
require(size>=0)
|
||||||
|
free.removeAll(address until address+size.toUInt())
|
||||||
|
if(name.isNotEmpty()) {
|
||||||
|
allocatedVariables[name] = when {
|
||||||
|
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||||
|
datatype.isString -> VarAllocation(address, datatype, size)
|
||||||
|
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun oneSeparateByteFree(address: UInt) = address in free && address-1u !in free && address+1u !in free
|
||||||
|
private fun sequentialFree(address: UInt, size: Int): Boolean {
|
||||||
|
require(size>0)
|
||||||
|
return free.containsAll((address until address+size.toUInt()).toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: this class is not yet used
|
||||||
|
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||||
|
private var nextLocation: UInt = region.first
|
||||||
|
|
||||||
|
override fun allocate(
|
||||||
|
name: String,
|
||||||
|
datatype: DataType,
|
||||||
|
numElements: Int?,
|
||||||
|
position: Position?,
|
||||||
|
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||||
|
|
||||||
|
val size: Int =
|
||||||
|
when {
|
||||||
|
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||||
|
datatype.isString -> numElements!!
|
||||||
|
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||||
|
datatype.isFloat -> {
|
||||||
|
if (options.floats) {
|
||||||
|
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||||
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
|
}
|
||||||
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(nextLocation<=region.last && (region.last + 1u - nextLocation) >= size.toUInt()) {
|
||||||
|
val result = Ok(VarAllocation(nextLocation, datatype, size))
|
||||||
|
nextLocation += size.toUInt()
|
||||||
|
result
|
||||||
|
} else
|
||||||
|
Err(MemAllocationError("no more free space in Golden RAM to allocate $size sequential bytes"))
|
||||||
|
}
|
||||||
|
}
|
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
|
||||||
|
val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
|
val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
|
||||||
|
val BitwiseOperators = arrayOf("&", "|", "^", "~")
|
||||||
|
val PrefixOperators = arrayOf("+", "-", "~", "not")
|
||||||
|
|
||||||
|
fun invertedComparisonOperator(operator: String) =
|
||||||
|
when (operator) {
|
||||||
|
"==" -> "!="
|
||||||
|
"!=" -> "=="
|
||||||
|
"<" -> ">="
|
||||||
|
">" -> "<="
|
||||||
|
"<=" -> ">"
|
||||||
|
">=" -> "<"
|
||||||
|
else -> null
|
||||||
|
}
|
27
codeCore/src/prog8/code/core/Position.kt
Normal file
27
codeCore/src/prog8/code/core/Position.kt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.source.SourceCode
|
||||||
|
import java.nio.file.InvalidPathException
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
|
||||||
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
|
fun toClickableStr(): String {
|
||||||
|
if(this===DUMMY)
|
||||||
|
return ""
|
||||||
|
if(SourceCode.isLibraryResource(file))
|
||||||
|
return "$file:$line:$startCol:"
|
||||||
|
return try {
|
||||||
|
val path = Path(file).absolute().normalize().toString()
|
||||||
|
"file://$path:$line:$startCol:"
|
||||||
|
} catch(_: InvalidPathException) {
|
||||||
|
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||||
|
"file://$file:$line:$startCol:"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DUMMY = Position("~dummy~", 0, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?)
|
139
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
139
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package prog8.code.optimize
|
||||||
|
|
||||||
|
import prog8.code.StExtSub
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
|
fun optimizeSimplifiedAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||||
|
if (!options.optimize)
|
||||||
|
return
|
||||||
|
while (errors.noErrors() &&
|
||||||
|
optimizeAssignTargets(program, st) + optimizeWordPlusTimesTwo(program, options) > 0) {
|
||||||
|
// keep rolling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||||
|
fun recurse(node: PtNode, depth: Int) {
|
||||||
|
if(act(node, depth))
|
||||||
|
node.children.forEach { recurse(it, depth+1) }
|
||||||
|
}
|
||||||
|
recurse(root, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
|
||||||
|
var changes = 0
|
||||||
|
walkAst(program) { node: PtNode, depth: Int ->
|
||||||
|
if(node is PtAssignment) {
|
||||||
|
val value = node.value
|
||||||
|
val functionName = when(value) {
|
||||||
|
is PtBuiltinFunctionCall -> value.name
|
||||||
|
is PtFunctionCall -> value.name
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if(functionName!=null) {
|
||||||
|
val stNode = st.lookup(functionName)
|
||||||
|
if (stNode is StExtSub) {
|
||||||
|
require(node.children.size==stNode.returns.size+1) {
|
||||||
|
"number of targets must match return values"
|
||||||
|
}
|
||||||
|
node.children.zip(stNode.returns).withIndex().forEach { (index, xx) ->
|
||||||
|
val target = xx.first as PtAssignTarget
|
||||||
|
val returnedRegister = xx.second.register.registerOrPair
|
||||||
|
if(returnedRegister!=null && !target.void && target.identifier!=null) {
|
||||||
|
if(isSame(target.identifier!!, xx.second.type, returnedRegister)) {
|
||||||
|
// output register is already identical to target register, so it can become void
|
||||||
|
val voidTarget = PtAssignTarget(true, target.position)
|
||||||
|
node.children[index] = voidTarget
|
||||||
|
voidTarget.parent = node
|
||||||
|
changes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
|
||||||
|
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
|
||||||
|
val index = node.parent.children.indexOf(node)
|
||||||
|
val voidCall = PtFunctionCall(functionName, true, DataType.forDt(BaseDataType.UNDEFINED), value.position)
|
||||||
|
value.children.forEach { voidCall.add(it) }
|
||||||
|
node.parent.children[index] = voidCall
|
||||||
|
voidCall.parent = node.parent
|
||||||
|
changes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
|
||||||
|
if(returnedRegister in Cx16VirtualRegisters) {
|
||||||
|
val regname = returnedRegister.name.lowercase()
|
||||||
|
val identifierRegName = identifier.name.substringAfterLast('.')
|
||||||
|
/*
|
||||||
|
cx16.r? UWORD
|
||||||
|
cx16.r?s WORD
|
||||||
|
cx16.r?L UBYTE
|
||||||
|
cx16.r?H UBYTE
|
||||||
|
cx16.r?sL BYTE
|
||||||
|
cx16.r?sH BYTE
|
||||||
|
*/
|
||||||
|
if(identifier.type.isByte && type.isByte) {
|
||||||
|
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||||
|
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(identifier.type.isWord && type.isWord) {
|
||||||
|
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||||
|
return identifierRegName.substring(2) in arrayOf("", "s")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false // there are no identifiers directly corresponding to cpu registers
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun optimizeWordPlusTimesTwo(program: PtProgram, options: CompilationOptions): Int {
|
||||||
|
if(options.compTarget.name== VMTarget.NAME)
|
||||||
|
return 0
|
||||||
|
var changes = 0
|
||||||
|
walkAst(program) { node: PtNode, depth: Int ->
|
||||||
|
if (node is PtBinaryExpression) {
|
||||||
|
if(node.operator=="*" && node.right.type.isWord && node.right.asConstValue()==2.0) {
|
||||||
|
TODO("optimize word + byte*2 (usually already replaced by w+b<<2)")
|
||||||
|
}
|
||||||
|
else if(node.operator=="<<" && node.right.asConstValue()==1.0) {
|
||||||
|
val typecast=node.left as? PtTypeCast
|
||||||
|
if(typecast!=null && typecast.type.isWord && typecast.value is PtIdentifier) {
|
||||||
|
val addition = node.parent as? PtBinaryExpression
|
||||||
|
if(addition!=null && (addition.operator=="+" || addition.operator=="-") && addition.type.isWord) {
|
||||||
|
// word + (byte<<1 as uword) (== word + byte*2) --> (word + (byte as word)) + (byte as word)
|
||||||
|
val parent = addition.parent
|
||||||
|
val index = parent.children.indexOf(addition)
|
||||||
|
val addFirst = PtBinaryExpression(addition.operator, addition.type, addition.position)
|
||||||
|
val addSecond = PtBinaryExpression(addition.operator, addition.type, addition.position)
|
||||||
|
if(addition.left===node)
|
||||||
|
addFirst.add(addition.right)
|
||||||
|
else
|
||||||
|
addFirst.add(addition.left)
|
||||||
|
addFirst.add(typecast)
|
||||||
|
addSecond.add(addFirst)
|
||||||
|
addSecond.add(typecast.copy())
|
||||||
|
parent.children[index] = addSecond
|
||||||
|
addSecond.parent = parent
|
||||||
|
changes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
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 java.nio.file.Path
|
||||||
|
import java.util.TreeMap
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
|
||||||
|
|
||||||
|
// 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.absolute().normalize()
|
||||||
|
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).absolute().normalize()
|
||||||
|
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>>()
|
||||||
|
}
|
160
codeCore/src/prog8/code/source/SourceCode.kt
Normal file
160
codeCore/src/prog8/code/source/SourceCode.kt
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package prog8.code.source
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.text.Normalizer
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
||||||
|
*/
|
||||||
|
sealed class SourceCode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this [SourceCode] instance was created as a [Resource]
|
||||||
|
*/
|
||||||
|
abstract val isFromResources: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this [SourceCode] instance was created as a [File]
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
abstract val name: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where this [SourceCode] instance came from.
|
||||||
|
* This can be one of the following:
|
||||||
|
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
|
||||||
|
* * `string:44c56085` if was created via [String]
|
||||||
|
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
|
||||||
|
*/
|
||||||
|
abstract val origin: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source code as plain string.
|
||||||
|
*/
|
||||||
|
abstract val text: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printable representation, deliberately does NOT return the actual text.
|
||||||
|
*/
|
||||||
|
final override fun toString() = "${this.javaClass.name}[${this.origin}]"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||||
|
*/
|
||||||
|
private const val LIBRARYFILEPREFIX = "library:"
|
||||||
|
private const val STRINGSOURCEPREFIX = "string:"
|
||||||
|
val curdir: Path = Path(".").absolute()
|
||||||
|
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
||||||
|
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a plain String into a [SourceCode] object.
|
||||||
|
* [origin] will be something like `string:44c56085`.
|
||||||
|
*/
|
||||||
|
class Text(origText: String): SourceCode() {
|
||||||
|
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||||
|
override val isFromResources = false
|
||||||
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = false
|
||||||
|
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||||
|
override val name = "<unnamed-text>"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get [SourceCode] from the file represented by the specified Path.
|
||||||
|
* 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.
|
||||||
|
* @throws NoSuchFileException if the file does not exist
|
||||||
|
* @throws FileSystemException if the file cannot be read
|
||||||
|
*/
|
||||||
|
internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
|
||||||
|
override val text: String
|
||||||
|
override val origin: String
|
||||||
|
override val name: String
|
||||||
|
override val isFromResources = false
|
||||||
|
override val isFromFilesystem = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
val normalized = path.normalize()
|
||||||
|
origin = relative(normalized).toString()
|
||||||
|
try {
|
||||||
|
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||||
|
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||||
|
name = normalized.toFile().nameWithoutExtension
|
||||||
|
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||||
|
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||||
|
} catch (iox: IOException) {
|
||||||
|
throw FileSystemException(normalized.toFile()).also { it.initCause(iox) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [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.
|
||||||
|
*/
|
||||||
|
internal class Resource(pathString: String): SourceCode() {
|
||||||
|
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
|
||||||
|
|
||||||
|
override val isFromResources = true
|
||||||
|
override val isFromFilesystem = false
|
||||||
|
override val isFromLibrary = true
|
||||||
|
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||||
|
override val text: String
|
||||||
|
override val name: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
val rscURL = object {}.javaClass.getResource(normalized)
|
||||||
|
if (rscURL == null) {
|
||||||
|
val rscRoot = object {}.javaClass.getResource("/")
|
||||||
|
throw NoSuchFileException(
|
||||||
|
java.io.File(normalized),
|
||||||
|
reason = "looked in resources rooted at $rscRoot"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||||
|
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||||
|
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||||
|
name = Path(pathString).toFile().nameWithoutExtension
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SourceCode for internally generated nodes (usually Modules)
|
||||||
|
*/
|
||||||
|
class Generated(override val name: String) : SourceCode() {
|
||||||
|
override val isFromResources: Boolean = false
|
||||||
|
override val isFromFilesystem: Boolean = false
|
||||||
|
override val isFromLibrary: Boolean = false
|
||||||
|
override val origin: String = name
|
||||||
|
override val text: String = "<generated code node, no text representation>"
|
||||||
|
}
|
||||||
|
}
|
73
codeCore/src/prog8/code/target/C128Target.kt
Normal file
73
codeCore/src/prog8/code/target/C128Target.kt
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.C128Zeropage
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||||
|
override val name = NAME
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
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) {
|
||||||
|
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?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
codeCore/src/prog8/code/target/C64Target.kt
Normal file
103
codeCore/src/prog8/code/target/C64Target.kt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.C64Zeropage
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
override val name = NAME
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "c64"
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if(selectedEmulator!=1) {
|
||||||
|
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||||
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(_: 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val CompilationTargets = listOf(
|
||||||
|
C64Target.NAME,
|
||||||
|
C128Target.NAME,
|
||||||
|
Cx16Target.NAME,
|
||||||
|
PETTarget.NAME,
|
||||||
|
VMTarget.NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
|
||||||
|
C64Target.NAME -> C64Target()
|
||||||
|
C128Target.NAME -> C128Target()
|
||||||
|
Cx16Target.NAME -> Cx16Target()
|
||||||
|
PETTarget.NAME -> PETTarget()
|
||||||
|
VMTarget.NAME -> VMTarget()
|
||||||
|
else -> throw IllegalArgumentException("invalid compilation target")
|
||||||
|
}
|
168
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
168
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
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 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, 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 = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr
|
||||||
|
|
||||||
|
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"),
|
||||||
|
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) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
86
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
86
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.CX16Zeropage
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
override val name = NAME
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
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) {
|
||||||
|
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, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
76
codeCore/src/prog8/code/target/Mflpt5.kt
Normal file
76
codeCore/src/prog8/code/target/Mflpt5.kt
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
|
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
|
const val FLOAT_MEM_SIZE = 5
|
||||||
|
|
||||||
|
val zero = Mflpt5(0u, 0u, 0u, 0u, 0u)
|
||||||
|
fun fromNumber(num: Number): Mflpt5 {
|
||||||
|
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||||
|
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||||
|
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||||
|
|
||||||
|
val flt = num.toDouble()
|
||||||
|
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||||
|
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||||
|
if (flt == 0.0)
|
||||||
|
return zero
|
||||||
|
|
||||||
|
val sign = if (flt < 0.0) 0x80L else 0x00L
|
||||||
|
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
||||||
|
var mantissa = flt.absoluteValue
|
||||||
|
|
||||||
|
// if mantissa is too large, shift right and adjust exponent
|
||||||
|
while (mantissa >= 0x100000000) {
|
||||||
|
mantissa /= 2.0
|
||||||
|
exponent++
|
||||||
|
}
|
||||||
|
// if mantissa is too small, shift left and adjust exponent
|
||||||
|
while (mantissa < 0x80000000) {
|
||||||
|
mantissa *= 2.0
|
||||||
|
exponent--
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
exponent < 0 -> zero // underflow, use zero instead
|
||||||
|
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||||
|
exponent == 0 -> zero
|
||||||
|
else -> {
|
||||||
|
val mantLong = mantissa.toLong()
|
||||||
|
Mflpt5(
|
||||||
|
exponent.toUByte(),
|
||||||
|
(mantLong.and(0x7f000000L) ushr 24).or(sign).toUByte(),
|
||||||
|
(mantLong.and(0x00ff0000L) ushr 16).toUByte(),
|
||||||
|
(mantLong.and(0x0000ff00L) ushr 8).toUByte(),
|
||||||
|
(mantLong.and(0x000000ffL)).toUByte()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toDouble(): Double {
|
||||||
|
if (this == zero) return 0.0
|
||||||
|
val exp = b0.toInt() - 128
|
||||||
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
|
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
||||||
|
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||||
|
return if (sign) -result else result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeFloatFillAsm(): String {
|
||||||
|
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||||
|
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||||
|
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||||
|
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||||
|
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||||
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
|
}
|
||||||
|
}
|
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) {
|
||||||
|
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||||
|
else return 2 // treat it as the size to store a string pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
|
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||||
|
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||||
|
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
|
else -> 2 * (numElements ?: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
72
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
72
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.encodings.Encoder
|
||||||
|
import prog8.code.target.zp.PETZeropage
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||||
|
override val name = NAME
|
||||||
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
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) {
|
||||||
|
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)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
130
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
130
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package prog8.code.target
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
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, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||||
|
override val name = NAME
|
||||||
|
override val defaultEncoding = Encoding.ISO
|
||||||
|
override val libraryPath = null
|
||||||
|
override val customLauncher: List<String> = emptyList()
|
||||||
|
override val additionalAssemblerOptions = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME = "virtual"
|
||||||
|
const val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||||
|
}
|
||||||
|
|
||||||
|
override val cpu = CpuType.VIRTUAL
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||||
|
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
|
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_END = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||||
|
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||||
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
|
override lateinit var golden: GoldenRam // not actually used
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number): String {
|
||||||
|
// little endian binary representation
|
||||||
|
val bits = num.toDouble().toBits().toULong()
|
||||||
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||||
|
return parts.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||||
|
val bits = num.toBits().toULong()
|
||||||
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||||
|
require(bytes.size==8) { "need 8 bytes" }
|
||||||
|
val b0 = bytes[0].toLong() shl (8*7)
|
||||||
|
val b1 = bytes[1].toLong() shl (8*6)
|
||||||
|
val b2 = bytes[2].toLong() shl (8*5)
|
||||||
|
val b3 = bytes[3].toLong() shl (8*4)
|
||||||
|
val b4 = bytes[4].toLong() shl (8*3)
|
||||||
|
val b5 = bytes[5].toLong() shl (8*2)
|
||||||
|
val b6 = bytes[6].toLong() shl (8*1)
|
||||||
|
val b7 = bytes[7].toLong() shl (8*0)
|
||||||
|
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
println("\nStarting Virtual Machine...")
|
||||||
|
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||||
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
|
val filename = programNameWithPath.name
|
||||||
|
if(programNameWithPath.isReadable()) {
|
||||||
|
vm.runProgram(programNameWithPath.readText())
|
||||||
|
} else {
|
||||||
|
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||||
|
if(withExt.isReadable())
|
||||||
|
vm.runProgram(withExt.readText())
|
||||||
|
else
|
||||||
|
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||||
|
if(dt.isArray) {
|
||||||
|
if(numElements==null) return 2 // treat it as a pointer size
|
||||||
|
return when(dt.sub) {
|
||||||
|
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||||
|
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||||
|
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
|
||||||
|
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
|
else -> throw IllegalArgumentException("invalid sub type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dt.isString) {
|
||||||
|
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||||
|
else return 2 // treat it as the size to store a string pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
|
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||||
|
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||||
|
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
|
else -> 2 * (numElements ?: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(irSource: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
214
codeCore/src/prog8/code/target/encodings/AtasciiEncoding.kt
Normal file
214
codeCore/src/prog8/code/target/encodings/AtasciiEncoding.kt
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
|
||||||
|
object AtasciiEncoding {
|
||||||
|
|
||||||
|
private val decodeTable: CharArray = charArrayOf(
|
||||||
|
// $00
|
||||||
|
'♥',
|
||||||
|
'├',
|
||||||
|
'\uf130', // 🮇 0x02 -> RIGHT ONE QUARTER BLOCK (CUS)
|
||||||
|
'┘',
|
||||||
|
'┤',
|
||||||
|
'┐',
|
||||||
|
'╱',
|
||||||
|
'╲',
|
||||||
|
'◢',
|
||||||
|
'▗',
|
||||||
|
'◣',
|
||||||
|
'▝',
|
||||||
|
'▘',
|
||||||
|
'\uf132', // 🮂 0x1d -> UPPER ONE QUARTER BLOCK (CUS)
|
||||||
|
'▂',
|
||||||
|
'▖',
|
||||||
|
|
||||||
|
// $10
|
||||||
|
'♣',
|
||||||
|
'┌',
|
||||||
|
'─',
|
||||||
|
'┼',
|
||||||
|
'•',
|
||||||
|
'▄',
|
||||||
|
'▎',
|
||||||
|
'┬',
|
||||||
|
'┴',
|
||||||
|
'▌',
|
||||||
|
'└',
|
||||||
|
'\u001b', // $1b = escape
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1c = cursor up
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1d = cursor down
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1e = cursor left
|
||||||
|
'\ufffe', // UNDEFINED CHAR. $1f = cursor right
|
||||||
|
|
||||||
|
// $20
|
||||||
|
' ',
|
||||||
|
'!',
|
||||||
|
'"',
|
||||||
|
'#',
|
||||||
|
'$',
|
||||||
|
'%',
|
||||||
|
'&',
|
||||||
|
'\'',
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
|
'*',
|
||||||
|
'+',
|
||||||
|
',',
|
||||||
|
'-',
|
||||||
|
'.',
|
||||||
|
'/',
|
||||||
|
|
||||||
|
// $30
|
||||||
|
'0',
|
||||||
|
'1',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4',
|
||||||
|
'5',
|
||||||
|
'6',
|
||||||
|
'7',
|
||||||
|
'8',
|
||||||
|
'9',
|
||||||
|
':',
|
||||||
|
';',
|
||||||
|
'<',
|
||||||
|
'=',
|
||||||
|
'>',
|
||||||
|
'?',
|
||||||
|
|
||||||
|
// $40
|
||||||
|
'@',
|
||||||
|
'A',
|
||||||
|
'B',
|
||||||
|
'C',
|
||||||
|
'D',
|
||||||
|
'E',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'I',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'M',
|
||||||
|
'N',
|
||||||
|
'O',
|
||||||
|
|
||||||
|
// $50
|
||||||
|
'P',
|
||||||
|
'Q',
|
||||||
|
'R',
|
||||||
|
'S',
|
||||||
|
'T',
|
||||||
|
'U',
|
||||||
|
'V',
|
||||||
|
'W',
|
||||||
|
'X',
|
||||||
|
'Y',
|
||||||
|
'Z',
|
||||||
|
'[',
|
||||||
|
'\\',
|
||||||
|
']',
|
||||||
|
'^',
|
||||||
|
'_',
|
||||||
|
|
||||||
|
// $60
|
||||||
|
'♦',
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
'e',
|
||||||
|
'f',
|
||||||
|
'g',
|
||||||
|
'h',
|
||||||
|
'i',
|
||||||
|
'j',
|
||||||
|
'k',
|
||||||
|
'l',
|
||||||
|
'm',
|
||||||
|
'n',
|
||||||
|
'o',
|
||||||
|
|
||||||
|
// $70
|
||||||
|
'p',
|
||||||
|
'q',
|
||||||
|
'r',
|
||||||
|
's',
|
||||||
|
't',
|
||||||
|
'u',
|
||||||
|
'v',
|
||||||
|
'w',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'z',
|
||||||
|
'♠',
|
||||||
|
'|',
|
||||||
|
'\u000c', // $7d -> FORM FEED (CLEAR SCREEN)
|
||||||
|
'\u0008', // $7e -> BACKSPACE
|
||||||
|
'\u0009', // $7f -> TAB
|
||||||
|
|
||||||
|
// $80-$ff are reversed video characters + various special characters.
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $90
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\n', // $9b -> EOL/RETURN
|
||||||
|
'\ufffe', // UNDEFINED $9c = DELETE LINE
|
||||||
|
'\ufffe', // UNDEFINED $9d = INSERT LINE
|
||||||
|
'\ufffe', // UNDEFINED $9e = CLEAR TAB STOP
|
||||||
|
'\ufffe', // UNDEFINED $9f = SET TAB STOP
|
||||||
|
// $a0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $b0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $c0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $d0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $e0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
// $f0
|
||||||
|
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\ufffe',
|
||||||
|
'\u0007', // $fd = bell/beep
|
||||||
|
'\u007f', // $fe = DELETE
|
||||||
|
'\ufffe' // UNDEFINED $ff = INSERT
|
||||||
|
)
|
||||||
|
|
||||||
|
private val encodeTable = decodeTable.withIndex().associate{it.value to it.index}
|
||||||
|
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> encodeTable.getValue(chr).toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(mapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
|
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
||||||
|
}
|
||||||
|
}
|
326
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
326
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
object Cp437Encoding {
|
||||||
|
val charset: Charset = Charset.forName("IBM437")
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
'\u00a0' -> 255u
|
||||||
|
'☺' -> 1u
|
||||||
|
'☻' -> 2u
|
||||||
|
'♥' -> 3u
|
||||||
|
'♦' -> 4u
|
||||||
|
'♣' -> 5u
|
||||||
|
'♠' -> 6u
|
||||||
|
'•' -> 7u
|
||||||
|
'◘' -> 8u
|
||||||
|
'○' -> 9u
|
||||||
|
'◙' -> 10u
|
||||||
|
'♂' -> 11u
|
||||||
|
'♀' -> 12u
|
||||||
|
'♪' -> 13u
|
||||||
|
'♫' -> 14u
|
||||||
|
'☼' -> 15u
|
||||||
|
'►' -> 16u
|
||||||
|
'◄' -> 17u
|
||||||
|
'↕' -> 18u
|
||||||
|
'‼' -> 19u
|
||||||
|
'¶' -> 20u
|
||||||
|
'§' -> 21u
|
||||||
|
'▬' -> 22u
|
||||||
|
'↨' -> 23u
|
||||||
|
'↑' -> 24u
|
||||||
|
'↓' -> 25u
|
||||||
|
'→' -> 26u
|
||||||
|
'←' -> 27u
|
||||||
|
'∟' -> 28u
|
||||||
|
'↔' -> 29u
|
||||||
|
'▲' -> 30u
|
||||||
|
'▼' -> 31u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> charset.encode(chr.toString())[0].toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(mapped)
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
codeCore/src/prog8/code/target/encodings/Encoder.kt
Normal file
47
codeCore/src/prog8/code/target/encodings/Encoder.kt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.IStringEncoding
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
|
||||||
|
object Encoder: IStringEncoding {
|
||||||
|
override val defaultEncoding: Encoding = Encoding.ISO
|
||||||
|
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
|
val coded = when(encoding) {
|
||||||
|
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||||
|
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||||
|
Encoding.ISO -> IsoEncoding.encode(str)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||||
|
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||||
|
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||||
|
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||||
|
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||||
|
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||||
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
|
}
|
||||||
|
return coded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||||
|
val decoded = when(encoding) {
|
||||||
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
|
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||||
|
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||||
|
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||||
|
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||||
|
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||||
|
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||||
|
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||||
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
|
}
|
||||||
|
return decoded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
42
codeCore/src/prog8/code/target/encodings/IsoEncoding.kt
Normal file
42
codeCore/src/prog8/code/target/encodings/IsoEncoding.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
open class IsoEncodingBase(charsetName: String) {
|
||||||
|
val charset: Charset = Charset.forName(charsetName)
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
'\u0000' -> 0u
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> charset.encode(chr.toString())[0].toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(mapped)
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object IsoEncoding: IsoEncodingBase("ISO-8859-15")
|
||||||
|
object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5")
|
||||||
|
object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16")
|
122
codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
Normal file
122
codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
|
object JapaneseCharacterConverter {
|
||||||
|
// adapted from https://github.com/raminduw/Japanese-Character-Converter
|
||||||
|
|
||||||
|
private val ZENKAKU_KATAKANA = charArrayOf(
|
||||||
|
'ァ', 'ア', 'ィ', 'イ', 'ゥ',
|
||||||
|
'ウ', 'ェ', 'エ', 'ォ', 'オ', 'カ', 'ガ', 'キ', 'ギ', 'ク', 'グ', 'ケ', 'ゲ',
|
||||||
|
'コ', 'ゴ', 'サ', 'ザ', 'シ', 'ジ', 'ス', 'ズ', 'セ', 'ゼ', 'ソ', 'ゾ', 'タ',
|
||||||
|
'ダ', 'チ', 'ヂ', 'ッ', 'ツ', 'ヅ', 'テ', 'デ', 'ト', 'ド', 'ナ', 'ニ', 'ヌ',
|
||||||
|
'ネ', 'ノ', 'ハ', 'バ', 'パ', 'ヒ', 'ビ', 'ピ', 'フ', 'ブ', 'プ', 'ヘ', 'ベ',
|
||||||
|
'ペ', 'ホ', 'ボ', 'ポ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ャ', 'ヤ', 'ュ', 'ユ',
|
||||||
|
'ョ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ヮ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン',
|
||||||
|
'ヴ', 'ヵ', 'ヶ'
|
||||||
|
)
|
||||||
|
|
||||||
|
private val HANKAKU_HIRAGANA = charArrayOf(
|
||||||
|
'ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え',
|
||||||
|
'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ',
|
||||||
|
'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ',
|
||||||
|
'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ',
|
||||||
|
'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と',
|
||||||
|
'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば',
|
||||||
|
'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ',
|
||||||
|
'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む',
|
||||||
|
'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ',
|
||||||
|
'ら', 'り', 'る', 'れ', 'ろ', 'ゎ', 'わ', 'ゐ',
|
||||||
|
'ゑ', 'を', 'ん', 'ゔ', 'ゕ', 'ゖ'
|
||||||
|
)
|
||||||
|
|
||||||
|
private val HANKAKU_KATAKANA = arrayOf(
|
||||||
|
"ァ", "ア", "ィ", "イ", "ゥ",
|
||||||
|
"ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ",
|
||||||
|
"ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ",
|
||||||
|
"ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド",
|
||||||
|
"ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ",
|
||||||
|
"ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ",
|
||||||
|
"モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ",
|
||||||
|
"ワ", "イ", "エ", "ヲ", "ン", "ヴ", "カ", "ケ"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val ZENKAKU_KATAKANA_FIRST_CHAR_CODE = ZENKAKU_KATAKANA.first().code
|
||||||
|
private val HANKAKU_HIRAGANA_FIRST_CHAR_CODE = HANKAKU_HIRAGANA.first().code
|
||||||
|
|
||||||
|
private fun zenkakuKatakanaToHankakuKatakana(c: Char): String = if (c in ZENKAKU_KATAKANA) HANKAKU_KATAKANA[c.code - ZENKAKU_KATAKANA_FIRST_CHAR_CODE] else c.toString()
|
||||||
|
private fun hankakuKatakanaToZenkakuKatakana(c: Char): Char = if (c in HANKAKU_HIRAGANA) ZENKAKU_KATAKANA[c.code - HANKAKU_HIRAGANA_FIRST_CHAR_CODE] else c
|
||||||
|
|
||||||
|
fun zenkakuKatakanaToHankakuKatakana(s: String): String = buildString {
|
||||||
|
for (element in s) {
|
||||||
|
val converted = hankakuKatakanaToZenkakuKatakana(element)
|
||||||
|
val convertedChar = zenkakuKatakanaToHankakuKatakana(converted)
|
||||||
|
append(convertedChar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object KatakanaEncoding {
|
||||||
|
val charset: Charset = Charset.forName("JIS_X0201")
|
||||||
|
|
||||||
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
val mapped = str.map { chr ->
|
||||||
|
when (chr) {
|
||||||
|
|
||||||
|
'\u0000' -> 0u
|
||||||
|
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||||
|
|
||||||
|
'♥' -> 0xe3u
|
||||||
|
'♦' -> 0xe4u
|
||||||
|
'♣' -> 0xe5u
|
||||||
|
'♠' -> 0xe6u
|
||||||
|
|
||||||
|
'大' -> 0xeau
|
||||||
|
'中' -> 0xebu
|
||||||
|
'小' -> 0xecu
|
||||||
|
'百' -> 0xedu
|
||||||
|
'千' -> 0xeeu
|
||||||
|
'万' -> 0xefu
|
||||||
|
'♪' -> 0xf0u
|
||||||
|
'土' -> 0xf1u
|
||||||
|
'金' -> 0xf2u
|
||||||
|
'木' -> 0xf3u
|
||||||
|
'水' -> 0xf4u
|
||||||
|
'火' -> 0xf5u
|
||||||
|
'月' -> 0xf6u
|
||||||
|
'日' -> 0xf7u
|
||||||
|
'時' -> 0xf8u
|
||||||
|
'分' -> 0xf9u
|
||||||
|
'秒' -> 0xfau
|
||||||
|
'年' -> 0xfbu
|
||||||
|
'円' -> 0xfcu
|
||||||
|
'人' -> 0xfdu
|
||||||
|
'生' -> 0xfeu
|
||||||
|
'〒' -> 0xffu
|
||||||
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(chr.code - 0x8000).toUByte()
|
||||||
|
}
|
||||||
|
else -> charset.encode(chr.toString())[0].toUByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(mapped)
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
|
return try {
|
||||||
|
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||||
|
} catch (ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,14 @@
|
|||||||
package prog8.compiler.target.cbm
|
package prog8.code.target.encodings
|
||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
import com.github.michaelbull.result.Err
|
||||||
import com.github.michaelbull.result.Ok
|
import com.github.michaelbull.result.Ok
|
||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import prog8.ast.antlr.escape
|
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
|
|
||||||
object Petscii {
|
object PetsciiEncoding {
|
||||||
|
|
||||||
// decoding: from Petscii/Screencodes (0-255) to unicode
|
// decoding: from Petscii/Screencodes (0-255) to Unicode
|
||||||
// character tables used from https://github.com/dj51d/cbmcodecs
|
// character tables used from https://github.com/irmen/cbmcodecs2
|
||||||
|
|
||||||
private val decodingPetsciiLowercase = charArrayOf(
|
private val decodingPetsciiLowercase = charArrayOf(
|
||||||
'\u0000', // 0x00 -> \u0000
|
'\u0000', // 0x00 -> \u0000
|
||||||
@ -25,7 +24,7 @@ object Petscii {
|
|||||||
'\ufffe', // 0x0A -> UNDEFINED
|
'\ufffe', // 0x0A -> UNDEFINED
|
||||||
'\ufffe', // 0x0B -> UNDEFINED
|
'\ufffe', // 0x0B -> UNDEFINED
|
||||||
'\ufffe', // 0x0C -> UNDEFINED
|
'\ufffe', // 0x0C -> UNDEFINED
|
||||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||||
'\u000e', // 0x0E -> SHIFT OUT
|
'\u000e', // 0x0E -> SHIFT OUT
|
||||||
'\ufffe', // 0x0F -> UNDEFINED
|
'\ufffe', // 0x0F -> UNDEFINED
|
||||||
'\ufffe', // 0x10 -> UNDEFINED
|
'\ufffe', // 0x10 -> UNDEFINED
|
||||||
@ -153,13 +152,13 @@ object Petscii {
|
|||||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||||
'\n' , // 0x8D -> LINE FEED
|
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||||
'\u000f', // 0x8E -> SHIFT IN
|
'\u000f', // 0x8E -> SHIFT IN
|
||||||
'\ufffe', // 0x8F -> UNDEFINED
|
'\ufffe', // 0x8F -> UNDEFINED
|
||||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||||
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
||||||
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
||||||
'\u000c', // 0x93 -> FORM FEED
|
'\u000c', // 0x93 -> FORM FEED (CLEAR SCREEN)
|
||||||
'\uf121', // 0x94 -> INSERT (CUS)
|
'\uf121', // 0x94 -> INSERT (CUS)
|
||||||
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
||||||
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
||||||
@ -195,7 +194,7 @@ object Petscii {
|
|||||||
'\u258e', // ▎ 0xB4 -> LEFT ONE QUARTER BLOCK
|
'\u258e', // ▎ 0xB4 -> LEFT ONE QUARTER BLOCK
|
||||||
'\u258d', // ▍ 0xB5 -> LEFT THREE EIGTHS BLOCK
|
'\u258d', // ▍ 0xB5 -> LEFT THREE EIGTHS BLOCK
|
||||||
'\uf131', // 0xB6 -> RIGHT THREE EIGHTHS BLOCK (CUS)
|
'\uf131', // 0xB6 -> RIGHT THREE EIGHTHS BLOCK (CUS)
|
||||||
'\uf132', // 0xB7 -> UPPER ONE QUARTER BLOCK (CUS)
|
'\uf132', // 🮂 0xB7 -> UPPER ONE QUARTER BLOCK (CUS)
|
||||||
'\uf133', // 0xB8 -> UPPER THREE EIGHTS BLOCK (CUS)
|
'\uf133', // 0xB8 -> UPPER THREE EIGHTS BLOCK (CUS)
|
||||||
'\u2583', // ▃ 0xB9 -> LOWER THREE EIGHTHS BLOCK
|
'\u2583', // ▃ 0xB9 -> LOWER THREE EIGHTHS BLOCK
|
||||||
'\u2713', // ✓ 0xBA -> CHECK MARK
|
'\u2713', // ✓ 0xBA -> CHECK MARK
|
||||||
@ -246,7 +245,7 @@ object Petscii {
|
|||||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
'\uf12f', // 0xE8 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
|
'\uf12f', // 0xE8 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
|
||||||
'\uf13a', // 0xE9 -> MEDIUM SHADE SLASHED RIGHT (CUS)
|
'\uf13a', // 0xE9 -> MEDIUM SHADE SLASHED RIGHT (CUS)
|
||||||
'\uf130', // 0xEA -> RIGHT ONE QUARTER BLOCK (CUS)
|
'\uf130', // 🮇 0xEA -> RIGHT ONE QUARTER BLOCK (CUS)
|
||||||
'\u251c', // ├ 0xEB -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
'\u251c', // ├ 0xEB -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||||
'\u2597', // ▗ 0xEC -> QUADRANT LOWER RIGHT
|
'\u2597', // ▗ 0xEC -> QUADRANT LOWER RIGHT
|
||||||
'\u2514', // └ 0xED -> BOX DRAWINGS LIGHT UP AND RIGHT
|
'\u2514', // └ 0xED -> BOX DRAWINGS LIGHT UP AND RIGHT
|
||||||
@ -284,7 +283,7 @@ object Petscii {
|
|||||||
'\ufffe', // 0x0A -> UNDEFINED
|
'\ufffe', // 0x0A -> UNDEFINED
|
||||||
'\ufffe', // 0x0B -> UNDEFINED
|
'\ufffe', // 0x0B -> UNDEFINED
|
||||||
'\ufffe', // 0x0C -> UNDEFINED
|
'\ufffe', // 0x0C -> UNDEFINED
|
||||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||||
'\u000e', // 0x0E -> SHIFT OUT
|
'\u000e', // 0x0E -> SHIFT OUT
|
||||||
'\ufffe', // 0x0F -> UNDEFINED
|
'\ufffe', // 0x0F -> UNDEFINED
|
||||||
'\ufffe', // 0x10 -> UNDEFINED
|
'\ufffe', // 0x10 -> UNDEFINED
|
||||||
@ -412,13 +411,13 @@ object Petscii {
|
|||||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||||
'\n' , // 0x8D -> LINE FEED
|
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||||
'\u000f', // 0x8E -> SHIFT IN
|
'\u000f', // 0x8E -> SHIFT IN
|
||||||
'\ufffe', // 0x8F -> UNDEFINED
|
'\ufffe', // 0x8F -> UNDEFINED
|
||||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||||
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
'\uf11e', // 0x91 -> CURSOR UP (CUS)
|
||||||
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
'\uf11b', // 0x92 -> REVERSE VIDEO OFF (CUS)
|
||||||
'\u000c', // 0x93 -> FORM FEED
|
'\u000c', // 0x93 -> FORM FEED (CLEAR SCREEN)
|
||||||
'\uf121', // 0x94 -> INSERT (CUS)
|
'\uf121', // 0x94 -> INSERT (CUS)
|
||||||
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
'\uf106', // 0x95 -> BROWN COLOR SWITCH (CUS)
|
||||||
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
'\uf107', // 0x96 -> LIGHT RED COLOR SWITCH (CUS)
|
||||||
@ -1062,22 +1061,26 @@ object Petscii {
|
|||||||
'}' -> '├'
|
'}' -> '├'
|
||||||
'|' -> '│'
|
'|' -> '│'
|
||||||
'\\' -> '╲'
|
'\\' -> '╲'
|
||||||
|
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||||
else -> chr
|
else -> chr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodePetscii(text: String, lowercase: Boolean = false): Result<List<Short>, CharConversionException> {
|
fun encodePetscii(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
val chr = replaceSpecial(chr3)
|
val chr = replaceSpecial(chr3)
|
||||||
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
||||||
return screencode?.toShort() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toShort()
|
(chr.code - 0x8000).toUByte()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}Petscii character for '${escape(chr.toString())}' (${chr.code})")
|
if(chr.isISOControl())
|
||||||
|
throw CharConversionException("no ${case}Petscii character for char #${chr.code}")
|
||||||
|
else
|
||||||
|
throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1086,7 +1089,7 @@ object Petscii {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1095,28 +1098,35 @@ object Petscii {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodePetscii(petscii: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
return petscii.map {
|
return try {
|
||||||
|
Ok(petscii.map {
|
||||||
val code = it.toInt()
|
val code = it.toInt()
|
||||||
if(code<0 || code>= decodingPetsciiLowercase.size)
|
if(code<0 || code>= decodingPetsciiLowercase.size)
|
||||||
throw CharConversionException("petscii $code out of range 0..${decodingPetsciiLowercase.size-1}")
|
throw CharConversionException("petscii $code out of range 0..${decodingPetsciiLowercase.size-1}")
|
||||||
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
||||||
}.joinToString("")
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
return Err(ce)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeScreencode(text: String, lowercase: Boolean = false): Result<List<Short>, CharConversionException> {
|
fun encodeScreencode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||||
val chr = replaceSpecial(chr3)
|
val chr = replaceSpecial(chr3)
|
||||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
return screencode?.toShort() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toShort()
|
(chr.code - 0x8000).toUByte()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}Screencode character for '${escape(chr.toString())}' (${chr.code})")
|
if(chr.isISOControl())
|
||||||
|
throw CharConversionException("no ${case}Screencode character for char #${chr.code}")
|
||||||
|
else
|
||||||
|
throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1125,7 +1135,7 @@ object Petscii {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1134,47 +1144,50 @@ object Petscii {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodeScreencode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||||
return screencode.map {
|
return try {
|
||||||
|
Ok(screencode.map {
|
||||||
val code = it.toInt()
|
val code = it.toInt()
|
||||||
if(code<0 || code>= decodingScreencodeLowercase.size)
|
if(code<0 || code>= decodingScreencodeLowercase.size)
|
||||||
throw CharConversionException("screencode $code out of range 0..${decodingScreencodeLowercase.size-1}")
|
throw CharConversionException("screencode $code out of range 0..${decodingScreencodeLowercase.size-1}")
|
||||||
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
||||||
}.joinToString("")
|
}.joinToString(""))
|
||||||
|
} catch(ce: CharConversionException) {
|
||||||
|
Err(ce)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Result<Short, CharConversionException> {
|
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
||||||
val code = when {
|
val code: UInt = when {
|
||||||
petscii_code < 0 -> return Err(CharConversionException("petscii code out of range"))
|
petsciicode <= 0x1fu -> petsciicode + 128u
|
||||||
petscii_code <= 0x1f -> petscii_code + 128
|
petsciicode <= 0x3fu -> petsciicode.toUInt()
|
||||||
petscii_code <= 0x3f -> petscii_code.toInt()
|
petsciicode <= 0x5fu -> petsciicode - 64u
|
||||||
petscii_code <= 0x5f -> petscii_code - 64
|
petsciicode <= 0x7fu -> petsciicode - 32u
|
||||||
petscii_code <= 0x7f -> petscii_code - 32
|
petsciicode <= 0x9fu -> petsciicode + 64u
|
||||||
petscii_code <= 0x9f -> petscii_code + 64
|
petsciicode <= 0xbfu -> petsciicode - 64u
|
||||||
petscii_code <= 0xbf -> petscii_code - 64
|
petsciicode <= 0xfeu -> petsciicode - 128u
|
||||||
petscii_code <= 0xfe -> petscii_code - 128
|
petsciicode == 255.toUByte() -> 95u
|
||||||
petscii_code == 255.toShort() -> 95
|
|
||||||
else -> return Err(CharConversionException("petscii code out of range"))
|
else -> return Err(CharConversionException("petscii code out of range"))
|
||||||
}
|
}
|
||||||
if(inverseVideo)
|
if(inverseVideo) {
|
||||||
return Ok((code or 0x80).toShort())
|
return Ok((code or 0x80u).toUByte())
|
||||||
return Ok(code.toShort())
|
}
|
||||||
|
return Ok(code.toUByte())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun scr2petscii(screencode: Short): Result<Short, CharConversionException> {
|
fun scr2petscii(screencode: UByte): Result<UByte, CharConversionException> {
|
||||||
val petscii = when {
|
val petscii: UInt = when {
|
||||||
screencode < 0 -> return Err(CharConversionException("screencode out of range"))
|
screencode <= 0x1fu -> screencode + 64u
|
||||||
screencode <= 0x1f -> screencode + 64
|
screencode <= 0x3fu -> screencode.toUInt()
|
||||||
screencode <= 0x3f -> screencode.toInt()
|
screencode <= 0x5du -> screencode +123u
|
||||||
screencode <= 0x5d -> screencode +123
|
screencode == 0x5e.toUByte() -> 255u
|
||||||
screencode == 0x5e.toShort() -> 255
|
screencode == 0x5f.toUByte() -> 223u
|
||||||
screencode == 0x5f.toShort() -> 223
|
screencode <= 0x7fu -> screencode + 64u
|
||||||
screencode <= 0x7f -> screencode + 64
|
screencode <= 0xbfu -> screencode - 128u
|
||||||
screencode <= 0xbf -> screencode - 128
|
screencode <= 0xfeu -> screencode - 64u
|
||||||
screencode <= 0xfe -> screencode - 64
|
screencode == 255.toUByte() -> 191u
|
||||||
screencode == 255.toShort() -> 191
|
|
||||||
else -> return Err(CharConversionException("screencode out of range"))
|
else -> return Err(CharConversionException("screencode out of range"))
|
||||||
}
|
}
|
||||||
return Ok(petscii.toShort())
|
return Ok(petscii.toUByte())
|
||||||
}
|
}
|
||||||
}
|
}
|
74
codeCore/src/prog8/code/target/zp/C128Zeropage.kt
Normal file
74
codeCore/src/prog8/code/target/zp/C128Zeropage.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
|
// reference: "Mapping the C128" zeropage chapter.
|
||||||
|
|
||||||
|
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||||
|
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||||
|
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
||||||
|
free.addAll(0x0au..0xffu)
|
||||||
|
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 -> {
|
||||||
|
free.addAll(0x0au..0x8fu) // BASIC variables
|
||||||
|
free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||||
|
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
||||||
|
}
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
||||||
|
free.addAll(0x1bu..0x23u)
|
||||||
|
free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
|
||||||
|
0x55u, 0x56u, 0x57u, 0x58u,
|
||||||
|
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,
|
||||||
|
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u
|
||||||
|
))
|
||||||
|
|
||||||
|
// 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
|
||||||
|
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||||
|
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||||
|
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||||
|
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val distinctFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
retainAllowed()
|
||||||
|
}
|
||||||
|
}
|
98
codeCore/src/prog8/code/target/zp/C64Zeropage.kt
Normal file
98
codeCore/src/prog8/code/target/zp/C64Zeropage.kt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
|
free.addAll(0x02u..0xffu)
|
||||||
|
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||||
|
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||||
|
} else {
|
||||||
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
free.addAll(arrayOf(
|
||||||
|
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,
|
||||||
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||||
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||||
|
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
|
// 0x90-0xfa is 'kernal work storage area'
|
||||||
|
).map{it.toUInt()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
|
free.removeAll(arrayOf(
|
||||||
|
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
||||||
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
|
).map{it.toUInt()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.zeropage != ZeropageType.DONTUSE) {
|
||||||
|
// add the free Zp addresses
|
||||||
|
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||||
|
free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
|
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||||
|
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||||
|
} else {
|
||||||
|
// don't use the zeropage at all
|
||||||
|
free.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val distinctFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
|
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||||
|
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
|
||||||
|
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)
|
||||||
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
|
for(reg in 0..15) {
|
||||||
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
free.remove((4+reg*2).toUInt())
|
||||||
|
free.remove((5+reg*2).toUInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
codeCore/src/prog8/code/target/zp/CX16Zeropage.kt
Normal file
68
codeCore/src/prog8/code/target/zp/CX16Zeropage.kt
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||||
|
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
free.addAll(0x22u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
free.addAll(0xa9u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.FLOATSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
free.addAll(0xd4u..0xffu)
|
||||||
|
}
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(0x22u..0x7fu)
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
58
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||||
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||||
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||||
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||||
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||||
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
codeCore/src/prog8/code/target/zp/PETZeropage.kt
Normal file
55
codeCore/src/prog8/code/target/zp/PETZeropage.kt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package prog8.code.target.zp
|
||||||
|
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
|
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||||
|
|
||||||
|
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||||
|
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||||
|
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE,
|
||||||
|
ZeropageType.DONTUSE
|
||||||
|
))
|
||||||
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
free.addAll(0x00u..0xffu)
|
||||||
|
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x00u..0xffu)
|
||||||
|
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||||
|
}
|
||||||
|
ZeropageType.FLOATSAFE,
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(0xb3u..0xbau) // TODO more?
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val distinctFree = free.distinct()
|
||||||
|
free.clear()
|
||||||
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
retainAllowed()
|
||||||
|
}
|
||||||
|
}
|
46
codeGenCpu6502/build.gradle.kts
Normal file
46
codeGenCpu6502/build.gradle.kts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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")
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,13 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
<orderEntry type="module" module-name="compilerAst" />
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||||
<orderEntry type="library" name="junit.jupiter" level="project" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
1720
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
1720
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
753
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
753
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
@ -0,0 +1,753 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.StConstant
|
||||||
|
import prog8.code.StMemVar
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtLabel
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
||||||
|
|
||||||
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
|
var linesByFour = getLinesBy(lines, 4)
|
||||||
|
|
||||||
|
var mods = optimizeIncDec(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeUselessPushPopStack(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeUnneededTempvarInAdd(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeTSBtoRegularOr(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
var linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
return numberOfOptimizations
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.isBranch() = this.startsWith("b")
|
||||||
|
private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty") || this.startsWith("stx")
|
||||||
|
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||||
|
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||||
|
|
||||||
|
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
|
||||||
|
|
||||||
|
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||||
|
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||||
|
if(modification.remove) {
|
||||||
|
if(modification.removeLabel)
|
||||||
|
lines.removeAt(modification.lineIndex)
|
||||||
|
else {
|
||||||
|
val line = lines[modification.lineIndex]
|
||||||
|
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
|
||||||
|
lines.removeAt(modification.lineIndex)
|
||||||
|
else if (haslabel(line)) {
|
||||||
|
val label = keeplabel(line)
|
||||||
|
if (label.isNotEmpty())
|
||||||
|
lines[modification.lineIndex] = label
|
||||||
|
else
|
||||||
|
lines.removeAt(modification.lineIndex)
|
||||||
|
} else lines.removeAt(modification.lineIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lines[modification.lineIndex] = modification.replacement!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun haslabel(line: String): Boolean {
|
||||||
|
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun keeplabel(line: String): String {
|
||||||
|
if(':' in line)
|
||||||
|
return line.substringBefore(':') + ':'
|
||||||
|
val splits = line.split('\t', ' ', limit=2)
|
||||||
|
return if(splits.size>1) splits[0] + ':' else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||||
|
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||||
|
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
|
private fun optimizeSameAssignments(
|
||||||
|
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||||
|
machine: ICompilationTarget,
|
||||||
|
symbolTable: SymbolTable
|
||||||
|
): List<Modification> {
|
||||||
|
|
||||||
|
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
||||||
|
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||||
|
// The better place to do this is in the Compiler instead and never create these types of assembly, but hey
|
||||||
|
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFourteen) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
val fourth = lines[3].value.trimStart()
|
||||||
|
val fifth = lines[4].value.trimStart()
|
||||||
|
val sixth = lines[5].value.trimStart()
|
||||||
|
val seventh = lines[6].value.trimStart()
|
||||||
|
val eighth = lines[7].value.trimStart()
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
|
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = fifth.substring(4)
|
||||||
|
val fourthvalue = sixth.substring(4)
|
||||||
|
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
|
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||||
|
val address1 = getAddressArg(first, symbolTable)
|
||||||
|
val address2 = getAddressArg(second, symbolTable)
|
||||||
|
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
|
||||||
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
|
mods.add(Modification(lines[5].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = third.substring(4)
|
||||||
|
if(firstvalue==secondvalue) {
|
||||||
|
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
||||||
|
val address = getAddressArg(first, symbolTable)
|
||||||
|
if(address==null || !machine.isIOAddress(address))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
|
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||||
|
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
|
val nineth = lines[8].value.trimStart()
|
||||||
|
val tenth = lines[9].value.trimStart()
|
||||||
|
val eleventh = lines[10].value.trimStart()
|
||||||
|
val twelveth = lines[11].value.trimStart()
|
||||||
|
val thirteenth = lines[12].value.trimStart()
|
||||||
|
val fourteenth = lines[13].value.trimStart()
|
||||||
|
|
||||||
|
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||||
|
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||||
|
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
|
// identical float init
|
||||||
|
mods.add(Modification(lines[7].index, true, null))
|
||||||
|
mods.add(Modification(lines[8].index, true, null))
|
||||||
|
mods.add(Modification(lines[9].index, true, null))
|
||||||
|
mods.add(Modification(lines[10].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var overlappingMods = false
|
||||||
|
/*
|
||||||
|
sta prog8_lib.retval_intermX ; remove
|
||||||
|
sty prog8_lib.retval_intermY ; remove
|
||||||
|
lda prog8_lib.retval_intermX ; remove
|
||||||
|
ldy prog8_lib.retval_intermY ; remove
|
||||||
|
sta A1
|
||||||
|
sty A2
|
||||||
|
*/
|
||||||
|
if(first.isStoreReg() && second.isStoreReg()
|
||||||
|
&& third.isLoadReg() && fourth.isLoadReg()
|
||||||
|
&& fifth.isStoreReg() && sixth.isStoreReg()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg2 = second[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
val reg4 = fourth[2]
|
||||||
|
val reg5 = fifth[2]
|
||||||
|
val reg6 = sixth[2]
|
||||||
|
if (reg1 == reg3 && reg1 == reg5 && reg2 == reg4 && reg2 == reg6) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
if(firstvalue.contains("prog8_lib.retval_interm") && secondvalue.contains("prog8_lib.retval_interm")
|
||||||
|
&& firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
overlappingMods = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
sty A2
|
||||||
|
lda A1 ; can be removed
|
||||||
|
ldy A2 ; can be removed if not followed by a branch instuction
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.isStoreReg() && second.isStoreReg()
|
||||||
|
&& third.isLoadReg() && fourth.isLoadReg()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg2 = second[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
val reg4 = fourth[2]
|
||||||
|
if(reg1==reg3 && reg2==reg4) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||||
|
val address = getAddressArg(first, symbolTable)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
if (!fifth.startsWith('b'))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
sty A2 ; ... or stz
|
||||||
|
lda A1 ; can be removed if not followed by a branch instruction
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.isStoreReg() && second.isStoreRegOrZero()
|
||||||
|
&& third.isLoadReg() && !fourth.isBranch()) {
|
||||||
|
val reg1 = first[2]
|
||||||
|
val reg3 = third[2]
|
||||||
|
if(reg1==reg3) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
if(firstvalue==thirdvalue) {
|
||||||
|
val address = getAddressArg(first, symbolTable)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A1
|
||||||
|
ldy A1 ; make tay
|
||||||
|
sta A1 ; remove
|
||||||
|
*/
|
||||||
|
if(!overlappingMods && first.startsWith("sta") && second.isLoadReg()
|
||||||
|
&& third.startsWith("sta") && second.length>4) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val thirdvalue = third.substring(4)
|
||||||
|
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||||
|
val address = getAddressArg(first, symbolTable)
|
||||||
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
|
overlappingMods = true
|
||||||
|
val reg2 = second[2]
|
||||||
|
mods.add(Modification(lines[1].index, false, " ta$reg2"))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sta A ; or stz double store, remove this first one
|
||||||
|
sta A ; or stz
|
||||||
|
However, this cannot be done relyably because 'A' could be a constant symbol referring to an I/O address.
|
||||||
|
We can't see that here and would otherwise delete valid double stores.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
|
||||||
|
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||||
|
// if Y isn't modified in between we can omit the second LDY:
|
||||||
|
// ldy #0
|
||||||
|
// lda (ptr),y
|
||||||
|
// ora #3 ; <-- instruction(s) that don't modify Y
|
||||||
|
// ldy #0 ; <-- can be removed
|
||||||
|
// sta (ptr),y
|
||||||
|
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFourteen) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
val fourth = lines[3].value.trimStart()
|
||||||
|
val fifth = lines[4].value.trimStart()
|
||||||
|
val sixth = lines[5].value.trimStart()
|
||||||
|
|
||||||
|
if(first.startsWith("ldy") && second.startsWith("lda") && fourth.startsWith("ldy") && fifth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val fourthvalue = fourth.substring(4)
|
||||||
|
val fifthvalue = fifth.substring(4)
|
||||||
|
if("y" !in third && firstvalue==fourthvalue && secondvalue==fifthvalue && secondvalue.endsWith(",y") && fifthvalue.endsWith(",y")) {
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(first.startsWith("ldy") && second.startsWith("lda") && fifth.startsWith("ldy") && sixth.startsWith("sta")) {
|
||||||
|
val firstvalue = first.substring(4)
|
||||||
|
val secondvalue = second.substring(4)
|
||||||
|
val fifthvalue = fifth.substring(4)
|
||||||
|
val sixthvalue = sixth.substring(4)
|
||||||
|
if("y" !in third && "y" !in fourth && firstvalue==fifthvalue && secondvalue==sixthvalue && secondvalue.endsWith(",y") && sixthvalue.endsWith(",y")) {
|
||||||
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+
|
||||||
|
[ optional: label_xxxx_shortcut line here]
|
||||||
|
beq label_xxxx_shortcut / bne label_xxxx_shortcut
|
||||||
|
or *_afterif labels.
|
||||||
|
|
||||||
|
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||||
|
*/
|
||||||
|
|
||||||
|
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
|
||||||
|
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||||
|
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||||
|
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
else if(fourth.startsWith(autoLabelPrefix) && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||||
|
if((fifth.startsWith("beq $autoLabelPrefix") || fifth.startsWith("bne $autoLabelPrefix")) &&
|
||||||
|
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeStoreLoadSame(
|
||||||
|
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||||
|
machine: ICompilationTarget,
|
||||||
|
symbolTable: SymbolTable
|
||||||
|
): List<Modification> {
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[1].value.trimStart()
|
||||||
|
val second = lines[2].value.trimStart()
|
||||||
|
val third = lines[3].value.trimStart()
|
||||||
|
|
||||||
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||||
|
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
||||||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("ldx ") && second.startsWith("ldx "))
|
||||||
|
) {
|
||||||
|
val attemptRemove =
|
||||||
|
if(third.isBranch()) {
|
||||||
|
// a branch instruction follows, we can only remove the load instruction if
|
||||||
|
// another load instruction of the same register precedes the store instruction
|
||||||
|
// (otherwise wrong cpu flags are used)
|
||||||
|
val loadinstruction = second.substring(0, 3)
|
||||||
|
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// no branch instruction follows, we can remove the load instruction
|
||||||
|
val address = getAddressArg(lines[2].value, symbolTable)
|
||||||
|
address==null || !machine.isIOAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attemptRemove) {
|
||||||
|
val firstLoc = first.substring(4).trimStart()
|
||||||
|
val secondLoc = second.substring(4).trimStart()
|
||||||
|
if (firstLoc == secondLoc)
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(first=="pha" && second=="pla" ||
|
||||||
|
first=="phx" && second=="plx" ||
|
||||||
|
first=="phy" && second=="ply" ||
|
||||||
|
first=="php" && second=="plp") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
} else if(first=="pha" && second=="plx") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tax"))
|
||||||
|
} else if(first=="pha" && second=="ply") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tay"))
|
||||||
|
} else if(first=="phx" && second=="pla") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " txa"))
|
||||||
|
} else if(first=="phy" && second=="pla") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tya"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||||
|
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||||
|
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||||
|
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||||
|
) {
|
||||||
|
val firstLoc = first.substring(4).trimStart()
|
||||||
|
val secondLoc = second.substring(4).trimStart()
|
||||||
|
if (firstLoc == secondLoc)
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||||
|
|
||||||
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
|
val loadArg = line.trimStart().substring(3).trim()
|
||||||
|
return when {
|
||||||
|
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
||||||
|
loadArg.startsWith('%') -> loadArg.substring(1).toUIntOrNull(2)
|
||||||
|
loadArg.startsWith('#') -> null
|
||||||
|
loadArg.startsWith('(') -> null
|
||||||
|
loadArg[0].isLetter() -> {
|
||||||
|
val identMatch = identifierRegex.find(loadArg)
|
||||||
|
if(identMatch!=null) {
|
||||||
|
val identifier = identMatch.value
|
||||||
|
when (val symbol = symbolTable.flat[identifier]) {
|
||||||
|
is StConstant -> symbol.value.toUInt()
|
||||||
|
is StMemVar -> symbol.address
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
else -> loadArg.substring(1).toUIntOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[0].value
|
||||||
|
val second = lines[1].value
|
||||||
|
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
||||||
|
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
||||||
|
|| (" ina" in first || "\tina" in first) && (" dea" in second || "\tdea" in second)
|
||||||
|
|| (" inc a" in first || "\tinc a" in first) && (" dec a" in second || "\tdec a" in second)
|
||||||
|
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
||||||
|
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)
|
||||||
|
|| (" dea" in first || "\tdea" in first) && (" ina" in second || "\tina" in second)
|
||||||
|
|| (" dec a" in first || "\tdec a" in first) && (" inc a" in second || "\tinc a" in second)) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
// rts + jmp -> remove jmp
|
||||||
|
// rts + bxx -> remove bxx
|
||||||
|
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||||
|
// and some other optimizations.
|
||||||
|
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
val first = lines[0].value
|
||||||
|
val second = lines[1].value
|
||||||
|
val third = lines[2].value
|
||||||
|
|
||||||
|
if(!haslabel(second)) {
|
||||||
|
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) ||
|
||||||
|
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
|
||||||
|
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
|
||||||
|
) {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
|
else if(" cmp #0" in second || "\tcmp #0" in second) {
|
||||||
|
// there are many instructions that modify A and set the bits...
|
||||||
|
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
|
||||||
|
if(" $instr" in first || "\t$instr" in first) {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
LDA NUM1
|
||||||
|
CMP NUM2
|
||||||
|
BCC LABEL
|
||||||
|
BEQ LABEL
|
||||||
|
|
||||||
|
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
|
||||||
|
|
||||||
|
LDA NUM2
|
||||||
|
CMP NUM1
|
||||||
|
BCS LABEL
|
||||||
|
*/
|
||||||
|
val tfirst = first.trimStart()
|
||||||
|
val tsecond = second.trimStart()
|
||||||
|
val tthird = lines[2].value.trimStart()
|
||||||
|
val tfourth = lines[3].value.trimStart()
|
||||||
|
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
|
||||||
|
val label = tthird.substring(4)
|
||||||
|
if(label==tfourth.substring(4)) {
|
||||||
|
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
|
||||||
|
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
|
||||||
|
mods += Modification(lines[2].index, false, " bcs $label")
|
||||||
|
mods += Modification(lines[3].index, true, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun sameLabel(branchInstr: String, jumpInstr: String, labelInstr: String): Boolean {
|
||||||
|
if('(' in jumpInstr) return false // indirect jump cannot be replaced
|
||||||
|
val label = labelInstr.trimEnd().substringBefore(':').substringBefore(' ').substringBefore('\t')
|
||||||
|
val branchLabel = branchInstr.trimStart().substring(3).trim()
|
||||||
|
return label==branchLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
// beq Label + jmp Addr + Label -> bne Addr
|
||||||
|
if((" jmp" in second || "\tjmp " in second) && haslabel(third)) {
|
||||||
|
if((" beq " in first || "\tbeq " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bne")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bne " in first || "\tbne " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "beq")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bcc " in first || "\tbcc " in first) && sameLabel(first, second, third)){
|
||||||
|
val branch = second.replace("jmp", "bcs")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bcs " in first || "\tbcs " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bcc")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bpl " in first || "\tbpl " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bmi")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bmi " in first || "\tbmi " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bpl")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bvc " in first || "\tbvc " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bvs")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bvs " in first || "\tbvs " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bvc")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
|
||||||
|
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
|
||||||
|
if(lines[0].value.trimStart().startsWith("ph$register")) {
|
||||||
|
if(lines[2].value.trimStart().startsWith("pl$register")) {
|
||||||
|
val second = lines[1].value.trimStart().take(6).lowercase()
|
||||||
|
if(register!in second
|
||||||
|
&& !second.startsWith("jsr")
|
||||||
|
&& !second.startsWith("pl")
|
||||||
|
&& !second.startsWith("ph")) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lines[3].value.trimStart().startsWith("pl$register")) {
|
||||||
|
val second = lines[1].value.trimStart().take(6).lowercase()
|
||||||
|
val third = lines[2].value.trimStart().take(6).lowercase()
|
||||||
|
if(register !in second && register !in third
|
||||||
|
&& !second.startsWith("jsr") && !third.startsWith("jsr")
|
||||||
|
&& !second.startsWith("pl") && !third.startsWith("pl")
|
||||||
|
&& !second.startsWith("ph") && !third.startsWith("ph")) {
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (lines in linesByFour) {
|
||||||
|
optimize('a', lines)
|
||||||
|
optimize('x', lines)
|
||||||
|
optimize('y', lines)
|
||||||
|
|
||||||
|
val first = lines[1].value.trimStart()
|
||||||
|
val second = lines[2].value.trimStart()
|
||||||
|
val third = lines[3].value.trimStart()
|
||||||
|
|
||||||
|
// phy + ldy + pla -> tya + ldy
|
||||||
|
// phx + ldx + pla -> txa + ldx
|
||||||
|
// pha + lda + pla -> nop
|
||||||
|
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, " tya"))
|
||||||
|
}
|
||||||
|
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, " txa"))
|
||||||
|
}
|
||||||
|
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
if(first.startsWith("lda") && second.startsWith("tsb") && third.startsWith("lda")) {
|
||||||
|
val operand1 = first.substring(3)
|
||||||
|
val operand2 = second.substring(3)
|
||||||
|
val operand3 = third.substring(3)
|
||||||
|
if(operand1!=operand2 && operand2==operand3) {
|
||||||
|
mods.add(Modification(lines[0].index, false, " lda $operand2"))
|
||||||
|
mods.add(Modification(lines[1].index, false, " ora $operand1"))
|
||||||
|
mods.add(Modification(lines[2].index, false, " sta $operand2"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
||||||
|
// this can be performed without the scratch variable: clc / adc something
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
val first = lines[0].value.trimStart()
|
||||||
|
val second = lines[1].value.trimStart()
|
||||||
|
val third = lines[2].value.trimStart()
|
||||||
|
val fourth = lines[3].value.trimStart()
|
||||||
|
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
|
||||||
|
if(fourth.substring(4)==first.substring(4)) {
|
||||||
|
mods.add(Modification(lines[0].index, false, " clc"))
|
||||||
|
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
32
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
32
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.ast.PtAsmSub
|
||||||
|
import prog8.code.core.Cx16VirtualRegisters
|
||||||
|
import prog8.code.core.RegisterOrPair
|
||||||
|
|
||||||
|
|
||||||
|
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
||||||
|
val order = mutableListOf<Int>()
|
||||||
|
// order is:
|
||||||
|
// 1) cx16 virtual word registers,
|
||||||
|
// 2) paired CPU registers,
|
||||||
|
// 3) single CPU registers (order Y,X,A),
|
||||||
|
// 4) floating point registers (FAC1, FAC2),
|
||||||
|
// 5) CPU Carry status flag
|
||||||
|
val args = sub.parameters.withIndex()
|
||||||
|
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||||
|
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
|
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||||
|
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 }
|
||||||
|
pairedRegs.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})
|
||||||
|
floatRegs.forEach { order += it.index }
|
||||||
|
rest.forEach { order += it.index }
|
||||||
|
require(order.size==sub.parameters.size)
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
159
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
159
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.ast.PtLabel
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C128Target
|
||||||
|
import prog8.code.target.C64Target
|
||||||
|
import prog8.code.target.PETTarget
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
internal class AssemblyProgram(
|
||||||
|
override val name: String,
|
||||||
|
outputDir: Path,
|
||||||
|
private val compTarget: ICompilationTarget) : IAssemblyProgram {
|
||||||
|
|
||||||
|
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||||
|
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||||
|
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||||
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
|
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||||
|
private val listFile = outputDir.resolve("$name.list")
|
||||||
|
|
||||||
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
|
|
||||||
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
|
when(options.output) {
|
||||||
|
OutputType.PRG -> {
|
||||||
|
// CBM machines .prg generation.
|
||||||
|
|
||||||
|
val command = mutableListOf("64tass", "--cbm-prg", "--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")
|
||||||
|
}
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
|
}
|
||||||
|
OutputType.XEX -> {
|
||||||
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
|
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
|
|
||||||
|
if(options.warnSymbolShadowing)
|
||||||
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
|
}
|
||||||
|
OutputType.RAW -> {
|
||||||
|
// Neo6502/headerless raw program generation.
|
||||||
|
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||||
|
|
||||||
|
if(options.warnSymbolShadowing)
|
||||||
|
command.add("-Wshadow")
|
||||||
|
else
|
||||||
|
command.add("-Wno-shadow")
|
||||||
|
|
||||||
|
if(options.asmQuiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
|
if(options.asmListfile)
|
||||||
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
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)) {
|
||||||
|
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||||
|
command.add("--cbm-prg")
|
||||||
|
} else {
|
||||||
|
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||||
|
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||||
|
}
|
||||||
|
|
||||||
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
|
assemblerCommand = command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||||
|
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||||
|
|
||||||
|
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||||
|
val result = proc.waitFor()
|
||||||
|
if (result == 0) {
|
||||||
|
removeGeneratedLabelsFromMonlist()
|
||||||
|
generateBreakpointList()
|
||||||
|
}
|
||||||
|
return result==0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
|
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
|
||||||
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
|
for (line in lines) {
|
||||||
|
if(pattern.matchEntire(line)==null)
|
||||||
|
it.write(line+"\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateBreakpointList() {
|
||||||
|
// builds list of breakpoints, appends to monitor list file
|
||||||
|
val breakpoints = mutableListOf<String>()
|
||||||
|
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
||||||
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
|
val match = pattern.matchEntire(line)
|
||||||
|
if (match != null)
|
||||||
|
breakpoints.add("break \$" + match.groupValues[1])
|
||||||
|
}
|
||||||
|
val num = breakpoints.size
|
||||||
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
|
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||||
|
breakpoints.add(2, "del")
|
||||||
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||||
|
}
|
||||||
|
}
|
1363
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
Normal file
1363
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user