mirror of
https://github.com/irmen/prog8.git
synced 2025-06-13 03:38:06 +00:00
Compare commits
2082 Commits
Author | SHA1 | Date | |
---|---|---|---|
8dc2e47507 | |||
daf7c3357c | |||
e8795859c5 | |||
bebe60b687 | |||
ddceec364e | |||
d067fa4b73 | |||
b5e51ab937 | |||
552e55c29f | |||
a228908c1a | |||
15fc3b6c04 | |||
0456badd02 | |||
399cf5118d | |||
a87f2640d3 | |||
a90ef274d7 | |||
341778ba67 | |||
ec50b5a007 | |||
31d84c8921 | |||
34bedbeef1 | |||
3b1b0985c1 | |||
368387e1a7 | |||
9da430ffeb | |||
cc063124cf | |||
3b37b89951 | |||
844b537d1e | |||
caf1d4a22a | |||
d8e244df99 | |||
548e421e27 | |||
322fa7ea69 | |||
cf7bea0985 | |||
25d7f8808f | |||
acc630972a | |||
6a33be3fd8 | |||
f5fc4e345c | |||
67231af623 | |||
e31ef6f06f | |||
09d188106a | |||
d8e2116481 | |||
435dfbb932 | |||
ba93966474 | |||
ea8d17cdb2 | |||
082265fb25 | |||
9e557ce8ac | |||
e5d9af75de | |||
31c1bf8bc5 | |||
37d4055036 | |||
78b1076110 | |||
0a3c748e41 | |||
ebf79ef9e2 | |||
99b9370178 | |||
d634061cd9 | |||
d59d8ff1fe | |||
53e442d509 | |||
f7cbfdff06 | |||
b28ee0819f | |||
522958e0e9 | |||
ccc6b56e35 | |||
7eb079050c | |||
2fdd5543b2 | |||
d04164c0a6 | |||
b047731f82 | |||
4d91f92a2e | |||
98505d27b1 | |||
cd63a58ad9 | |||
170f8dd092 | |||
619dcb6a84 | |||
99ae8ea52e | |||
dc031c30eb | |||
1e702439b7 | |||
8debc42381 | |||
532d719089 | |||
b40860aca4 | |||
2cbe6b5f7f | |||
d2cc7ccdfa | |||
2cb183c6d8 | |||
84026b105f | |||
a4d0589f10 | |||
e375f6afce | |||
5a7bc04816 | |||
bd1894580e | |||
9e694c0337 | |||
c82586db28 | |||
dd2d466350 | |||
830da8de0a | |||
4e5ee333c8 | |||
9df899eb63 | |||
ca7491a702 | |||
1a07129865 | |||
4fbd67ff99 | |||
5bc6c50f42 | |||
063de3801d | |||
ae65266a4a | |||
8ed2401e0b | |||
d2e8ee8269 | |||
1f996e3b8b | |||
7108b74105 | |||
801fe1b604 | |||
fb44c87597 | |||
6b9cdbd482 | |||
0ab98033b5 | |||
14a2b96609 | |||
f829b689db | |||
dfda8b7ed5 | |||
4388466451 | |||
5c2f509a52 | |||
59582f5210 | |||
e2a8bdbdfb | |||
0916b943da | |||
9c7ebc883c | |||
0ee42b9aa0 | |||
37b3868ca3 | |||
a6835ce3f0 | |||
69c96ad99b | |||
b72877d59d | |||
05eb15d4f7 | |||
f1fec37c79 | |||
73f6880ff8 | |||
8a53742f31 | |||
9be40e85ff | |||
61079c1eb7 | |||
1075ee8fc3 | |||
a28b265197 | |||
20e534c468 | |||
da7aa5dc49 | |||
8f2a43ca0a | |||
d0909d7810 | |||
1641999d20 | |||
e16452037c | |||
344d79684a | |||
573a1d9b7b | |||
25ab57580c | |||
a332e0e3d1 | |||
376f1cb139 | |||
90f80558d7 | |||
e281994898 | |||
29fac122e1 | |||
1dc412eb90 | |||
3770a4fe0c | |||
79cda544c8 | |||
f04b97d890 | |||
3e9b4ccc45 | |||
2c3d838dd8 | |||
7668a3c660 | |||
5dd45b714a | |||
8b08895d0f | |||
8f8d99e3ed | |||
23474360ec | |||
81c255c450 | |||
ef23d52ed7 | |||
220ab773aa | |||
e3e5bff7bb | |||
7b9a841b2a | |||
40423911ef | |||
582a70b046 | |||
5b63590ebf | |||
2b6510dc19 | |||
9d49589d73 | |||
125b66c929 | |||
5255f1c052 | |||
a6ba05d60c | |||
41e963b04b | |||
6ff75bef29 | |||
72c16d0d32 | |||
94653e5c8c | |||
3e2b2a698d | |||
ae04f5aee8 | |||
5c56267662 | |||
e55ce5504e | |||
fb1e89d9ef | |||
bc550a4549 | |||
ebdea9cf76 | |||
09ec508f82 | |||
d06e9ea7f6 | |||
a36bdc54fd | |||
0814ea9711 | |||
daefe839d8 | |||
e6088dd315 | |||
fc03d6f332 | |||
2aeb7a838e | |||
99ff5dd078 | |||
49982b49b6 | |||
fd39c22616 | |||
9e79722a7f | |||
17334a1c58 | |||
c7f0ff11ac | |||
cd2cc89e6a | |||
069143092d | |||
efd41260f2 | |||
8d2410622c | |||
60554389b3 | |||
a940dc7d43 | |||
06ca68a625 | |||
5b58e5b158 | |||
74dd8fe80b | |||
75ddcda5f3 | |||
216825b98a | |||
a96defab86 | |||
0864b0a1b7 | |||
8b158d9240 | |||
f335251c2b | |||
67bc0b6931 | |||
e646dd1ed1 | |||
2b7947f9b0 | |||
ec0cfb4b3f | |||
9cdf53019c | |||
1a04a3eb3a | |||
105d3995e0 | |||
8ce3204f93 | |||
d0f15f1285 | |||
66d6f67120 | |||
a106c88054 | |||
ee784e1ccc | |||
bb75be0b44 | |||
2478aea316 | |||
1e17df5296 | |||
8583a96519 | |||
d0c184c7de | |||
0191acb2b3 | |||
277a1a32b2 | |||
7a13f57ab0 | |||
0c882836d9 | |||
228be5cd04 | |||
08cd2fd6e8 | |||
bc7b086f0f | |||
e8f3af6981 | |||
f9c7c7dab7 | |||
09a17743ad | |||
4f096a7511 | |||
2ab2130000 | |||
66558f7638 | |||
a6f9ed07e7 | |||
7268a8736f | |||
8f6b5676d7 | |||
ca9422bbe9 | |||
35d9412559 | |||
f071c07dd9 | |||
e5ff3c1ff3 | |||
f0e8ff0326 | |||
3b5cda85ff | |||
420793f9e2 | |||
cf1dbaf0d8 | |||
d187cef6b7 | |||
3b3616afda | |||
0ffebc25d0 | |||
478e2b4ebd | |||
a56ae7539a | |||
407773bda2 | |||
823eaa8918 | |||
a2be42c5ca | |||
a76b8d66ff | |||
b7f47d354f | |||
5d33c93af9 | |||
4db6859f3f | |||
45fe1bb16e | |||
b014facbd3 | |||
3b4b37f16b | |||
68d5983a14 | |||
f2cfcfdf31 | |||
10b9162dc5 | |||
c84cc8f8c9 | |||
78c71bbf0e | |||
37c2c1bf0b | |||
c8996418da | |||
76b29aa629 | |||
ee521793f8 | |||
f42e12bc13 | |||
427451a23f | |||
af7930d494 | |||
e2882d37bf | |||
942d3ee640 | |||
7b4a82b91a | |||
056c0a24d9 | |||
827df04b32 | |||
e174b31344 | |||
49959af752 | |||
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 |
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/discord.xml
|
||||
.idea/developer-tools.xml
|
||||
.idea/usage.statistics.xml
|
||||
.idea/shelf/
|
||||
build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
@ -11,16 +15,17 @@ output/
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
docs/build
|
||||
out/
|
||||
parser/**/*.interp
|
||||
parser/**/*.tokens
|
||||
parser/**/*.java
|
||||
compiler/src/prog8/buildversion/*
|
||||
*.py[cod]
|
||||
*.egg
|
||||
*.egg-info
|
||||
.eggs/
|
||||
/MANIFEST
|
||||
.tox/
|
||||
.kotlin/
|
||||
__pycache__/
|
||||
parser.out
|
||||
parsetab.py
|
||||
@ -29,6 +34,6 @@ parsetab.py
|
||||
compiler/lib/
|
||||
|
||||
.gradle
|
||||
/prog8compiler.jar
|
||||
sd*.img
|
||||
*.d64
|
||||
|
||||
|
1457
.idea/inspectionProfiles/Project_Default.xml
generated
1457
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
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">
|
||||
<library name="KotlinJavaRuntime">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/antlr_antlr4.xml
generated
9
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,16 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr.antlr4" type="repository">
|
||||
<properties maven-id="org.antlr:antlr4:4.10.1">
|
||||
<properties maven-id="org.antlr:antlr4:4.13.2">
|
||||
<exclude>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.10.1/antlr4-4.10.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.10.1/antlr4-runtime-4.10.1.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.13.2/antlr4-runtime-4.13.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.3/ST4-4.3.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/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<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.24.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
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>
|
25
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
25
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
@ -1,21 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
|
||||
<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.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.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$/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/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.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 />
|
||||
|
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>
|
24
.idea/libraries/io_kotest_property_jvm.xml
generated
24
.idea/libraries/io_kotest_property_jvm.xml
generated
@ -1,24 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.3.2/kotest-property-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.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.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
70
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
70
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
@ -1,51 +1,39 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
|
||||
<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.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.3.2/kotest-framework-engine-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.jar!/" />
|
||||
<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.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.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$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.3/mockk-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.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/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.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/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.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.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.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 />
|
||||
|
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">
|
||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.4" />
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.4/kotlinx-cli-jvm-0.3.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
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">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/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>
|
||||
</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.36" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
17
.idea/libraries/takes.xml
generated
17
.idea/libraries/takes.xml
generated
@ -1,17 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="takes" type="repository">
|
||||
<properties maven-id="org.takes:takes:1.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.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!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@ -4,22 +4,26 @@
|
||||
<option name="perGrammarGenerationSettings">
|
||||
<list>
|
||||
<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="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
||||
<option name="libDir" value="" />
|
||||
<option name="encoding" value="" />
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<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="11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
6
.idea/modules.xml
generated
6
.idea/modules.xml
generated
@ -2,6 +2,8 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||
@ -10,12 +12,12 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
@ -7,23 +7,21 @@ version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
# You can also specify other tool versions:
|
||||
# nodejs: "16"
|
||||
# rust: "1.55"
|
||||
# golang: "1.17"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
python: "3.12"
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
fail_on_warning: true
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
@ -1,35 +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:
|
||||
|
||||
(note: all these steps have been implemented, rejected or otherwise solved now.)
|
||||
|
||||
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
|
||||
|
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"
|
||||
|
81
README.md
81
README.md
@ -1,3 +1,7 @@
|
||||
[](https://ko-fi.com/H2H6S0FFF)
|
||||
|
||||
PayPal: [](https://paypal.me/irmendejong)
|
||||
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
@ -9,11 +13,37 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
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
|
||||
----------------
|
||||
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||
@ -26,26 +56,32 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||
What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- reduction of source code length over raw assembly
|
||||
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||
- modularity, symbol scoping, subroutines
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because it's compiled to native machine code
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be configured to execute in ROM
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with input parameters and result values
|
||||
- high-level program optimizations
|
||||
- small program boilerplate/compilersupport overhead
|
||||
- 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
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||
- ``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
|
||||
- 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
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
|
||||
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings
|
||||
- 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:*
|
||||
|
||||
@ -56,9 +92,11 @@ What does Prog8 provide?
|
||||
|
||||
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||
- 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)
|
||||
|
||||
|
||||
@ -76,7 +114,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
|
||||
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
|
||||
@ -86,14 +129,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
ubyte[256] sieve
|
||||
bool[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
sys.memset(sieve, 256, false) ; clear the sieve
|
||||
sys.memset(sieve, 256, 0) ; clear the sieve
|
||||
txt.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
repeat {
|
||||
@ -129,9 +171,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
when compiled an ran on a C-64 you'll get:
|
||||
|
||||

|
||||
|
@ -10,8 +10,15 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||
<orderEntry type="library" name="github.hypfvieh.dbus.java" level="project" />
|
||||
<orderEntry type="library" name="slf4j.simple" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/bsh-3.0.0-SNAPSHOT.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
65
beanshell/build.gradle.kts
Normal file
65
beanshell/build.gradle.kts
Normal file
@ -0,0 +1,65 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
val serverMainClassName = "prog8lsp.MainKt"
|
||||
val applicationName = "prog8-beanshell"
|
||||
|
||||
application {
|
||||
mainClass.set(serverMainClassName)
|
||||
description = "Code completions, diagnostics and more for Prog8"
|
||||
// applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version")
|
||||
applicationDistribution.into("bin") {
|
||||
filePermissions {
|
||||
user {
|
||||
read=true
|
||||
execute=true
|
||||
write=true
|
||||
}
|
||||
other.execute = true
|
||||
group.execute = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(files("lib/bsh-3.0.0-SNAPSHOT.jar"))
|
||||
}
|
||||
|
||||
configurations.forEach { config ->
|
||||
config.resolutionStrategy {
|
||||
preferProjectModules()
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDir("src")
|
||||
resources.srcDir("resources")
|
||||
}
|
||||
|
||||
tasks.startScripts {
|
||||
applicationName = "prog8-beanshell"
|
||||
}
|
||||
|
||||
tasks.register<Exec>("fixFilePermissions") {
|
||||
// When running on macOS or Linux the start script
|
||||
// needs executable permissions to run.
|
||||
|
||||
onlyIf { !System.getProperty("os.name").lowercase().contains("windows") }
|
||||
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
|
||||
}
|
||||
|
||||
tasks.installDist {
|
||||
finalizedBy("fixFilePermissions")
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
finalizedBy("installDist")
|
||||
}
|
BIN
beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
Normal file
BIN
beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
Normal file
Binary file not shown.
48
beanshell/src/prog8beanshell/CommandLineReader.kt
Normal file
48
beanshell/src/prog8beanshell/CommandLineReader.kt
Normal file
@ -0,0 +1,48 @@
|
||||
package prog8beanshell
|
||||
|
||||
import java.io.FilterReader
|
||||
import java.io.Reader
|
||||
|
||||
|
||||
class CommandLineReader(val input: Reader): FilterReader(input) {
|
||||
private val normal = 0
|
||||
private val lastCharNL = 1
|
||||
private val sentSemi = 2
|
||||
private var state = lastCharNL
|
||||
|
||||
override fun read(): Int {
|
||||
if (state == sentSemi) {
|
||||
this.state = lastCharNL
|
||||
return 10
|
||||
} else {
|
||||
var b = input.read()
|
||||
while(b==13) b = input.read()
|
||||
|
||||
if (b == 10) {
|
||||
if (this.state == lastCharNL) {
|
||||
b = 59
|
||||
this.state = sentSemi
|
||||
} else {
|
||||
this.state = lastCharNL
|
||||
}
|
||||
} else {
|
||||
this.state = normal
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun read(buff: CharArray, off: Int, len: Int): Int {
|
||||
val b = read()
|
||||
if (b == -1) {
|
||||
return -1
|
||||
} else {
|
||||
buff[off] = b.toChar()
|
||||
return 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
23
beanshell/src/prog8beanshell/Main.kt
Normal file
23
beanshell/src/prog8beanshell/Main.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package prog8beanshell
|
||||
|
||||
import bsh.FileReader
|
||||
import bsh.Interpreter
|
||||
|
||||
|
||||
class BeanshellInterpreter {
|
||||
|
||||
fun run(symbols: Map<String, Any>) {
|
||||
val interpreter = Interpreter(CommandLineReader(FileReader(System.`in`)), System.out, System.err, true)
|
||||
interpreter.setExitOnEOF(false)
|
||||
symbols.forEach { (name, value) -> interpreter.set(name, value) }
|
||||
interpreter.run()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val i = BeanshellInterpreter()
|
||||
i.run(mapOf(
|
||||
"env" to System.getenv(),
|
||||
"args" to args
|
||||
))
|
||||
}
|
10
benchmark-program/Makefile
Normal file
10
benchmark-program/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
.PHONY: clean run
|
||||
|
||||
run:
|
||||
prog8c -target cx16 benchmark.p8
|
||||
x16emu -run -prg benchmark.prg -warp
|
||||
|
||||
clean:
|
||||
rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z
|
||||
|
||||
|
109
benchmark-program/b_3d.p8
Normal file
109
benchmark-program/b_3d.p8
Normal file
@ -0,0 +1,109 @@
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
rotate3d {
|
||||
const ubyte WIDTH = 40
|
||||
const ubyte HEIGHT = 30
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
|
||||
uword anglex
|
||||
uword angley
|
||||
uword anglez
|
||||
uword frames
|
||||
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
matrix_math.rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
||||
draw_edges() ; doesn't really draw anything in the benchmark, but does do the screen calculations
|
||||
anglex+=500
|
||||
angley+=215
|
||||
anglez+=453
|
||||
frames++
|
||||
}
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
sub draw_edges() {
|
||||
|
||||
; plot the points of the 3d cube
|
||||
; first the points on the back, then the points on the front (painter algorithm)
|
||||
|
||||
ubyte @zp i
|
||||
word @zp rz
|
||||
word @zp persp
|
||||
byte @shared sx
|
||||
byte @shared sy
|
||||
|
||||
for i in 0 to len(matrix_math.xcoor)-1 {
|
||||
rz = matrix_math.rotatedz[i]
|
||||
if rz >= 10 {
|
||||
persp = 600 + rz/64
|
||||
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
|
||||
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
|
||||
;; txt.setcc(sx as ubyte, sy as ubyte, 46, 7)
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 to len(matrix_math.xcoor)-1 {
|
||||
rz = matrix_math.rotatedz[i]
|
||||
if rz < 10 {
|
||||
persp = 600 + rz/64
|
||||
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
|
||||
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
|
||||
;; txt.setcc(sx as ubyte, sy as ubyte, 81, 7)
|
||||
}
|
||||
}
|
||||
|
||||
txt.chrout('.')
|
||||
}
|
||||
}
|
||||
|
||||
matrix_math {
|
||||
; vertices
|
||||
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
|
||||
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
|
||||
word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
|
||||
|
||||
; storage for rotated coordinates
|
||||
word[len(xcoor)] rotatedx
|
||||
word[len(ycoor)] rotatedy
|
||||
word[len(zcoor)] rotatedz
|
||||
|
||||
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
|
||||
; rotate around origin (0,0,0)
|
||||
|
||||
; set up the 3d rotation matrix values
|
||||
word wcosa = math.cos8(ax)
|
||||
word wsina = math.sin8(ax)
|
||||
word wcosb = math.cos8(ay)
|
||||
word wsinb = math.sin8(ay)
|
||||
word wcosc = math.cos8(az)
|
||||
word wsinc = math.sin8(az)
|
||||
|
||||
word wcosa_sinb = wcosa*wsinb / 128
|
||||
word wsina_sinb = wsina*wsinb / 128
|
||||
|
||||
word Axx = wcosa*wcosb / 128
|
||||
word Axy = (wcosa_sinb*wsinc - wsina*wcosc) / 128
|
||||
word Axz = (wcosa_sinb*wcosc + wsina*wsinc) / 128
|
||||
word Ayx = wsina*wcosb / 128
|
||||
word Ayy = (wsina_sinb*wsinc + wcosa*wcosc) / 128
|
||||
word Ayz = (wsina_sinb*wcosc - wcosa*wsinc) / 128
|
||||
word Azx = -wsinb
|
||||
word Azy = wcosb*wsinc / 128
|
||||
word Azz = wcosb*wcosc / 128
|
||||
|
||||
ubyte @zp i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
|
||||
rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
89
benchmark-program/b_adpcm.p8
Normal file
89
benchmark-program/b_adpcm.p8
Normal file
@ -0,0 +1,89 @@
|
||||
adpcm {
|
||||
|
||||
sub decode_benchmark(uword max_time) -> uword {
|
||||
uword num_blocks
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
adpcm.init(0,0)
|
||||
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
|
||||
repeat 252/2 {
|
||||
unroll 2 {
|
||||
ubyte @zp nibble = @(nibbles_ptr)
|
||||
adpcm.decode_nibble(nibble & 15) ; first word (note: upper nibble needs to be zero!)
|
||||
adpcm.decode_nibble(nibble>>4) ; second word (note: upper nibble is zero, after the shifts.)
|
||||
nibbles_ptr++
|
||||
}
|
||||
}
|
||||
num_blocks++
|
||||
txt.chrout('.')
|
||||
}
|
||||
|
||||
return num_blocks
|
||||
}
|
||||
|
||||
; IMA ADPCM decoder. Supports mono and stereo streams.
|
||||
|
||||
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
|
||||
uword[] t_step = [
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||
32767]
|
||||
|
||||
uword @requirezp predict ; decoded 16 bit pcm sample for first channel.
|
||||
ubyte @requirezp index
|
||||
uword @requirezp pstep
|
||||
|
||||
sub init(uword startPredict, ubyte startIndex) {
|
||||
; initialize first decoding channel.
|
||||
predict = startPredict
|
||||
index = startIndex
|
||||
pstep = t_step[index]
|
||||
}
|
||||
|
||||
sub decode_nibble(ubyte @zp nibble) {
|
||||
; Decoder for a single nibble for the first channel. (value of 'nibble' needs to be strictly 0-15 !)
|
||||
; This is the hotspot of the decoder algorithm!
|
||||
; Note that the generated assembly from this is pretty efficient,
|
||||
; rewriting it by hand in asm seems to improve it only ~10%.
|
||||
cx16.r0s = 0 ; difference
|
||||
if nibble & %0100 !=0
|
||||
cx16.r0s += pstep
|
||||
pstep >>= 1
|
||||
if nibble & %0010 !=0
|
||||
cx16.r0s += pstep
|
||||
pstep >>= 1
|
||||
if nibble & %0001 !=0
|
||||
cx16.r0s += pstep
|
||||
pstep >>= 1
|
||||
cx16.r0s += pstep
|
||||
if nibble & %1000 !=0
|
||||
predict -= cx16.r0
|
||||
else
|
||||
predict += cx16.r0
|
||||
|
||||
; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word
|
||||
; but for speed reasons we only work with 16 bit words here all the time (with possible clipping error)
|
||||
; if predicted > 32767:
|
||||
; predicted = 32767
|
||||
; elif predicted < -32767:
|
||||
; predicted = - 32767
|
||||
|
||||
index += t_index[nibble] as ubyte
|
||||
if_neg
|
||||
index = 0
|
||||
else if index >= len(t_step)-1
|
||||
index = len(t_step)-1
|
||||
pstep = t_step[index]
|
||||
}
|
||||
}
|
111
benchmark-program/b_circles.p8
Normal file
111
benchmark-program/b_circles.p8
Normal file
@ -0,0 +1,111 @@
|
||||
%import gfx_lores
|
||||
%import math
|
||||
|
||||
circles {
|
||||
const ubyte MAX_NUM_CIRCLES = 80
|
||||
const ubyte GROWTH_RATE = 4
|
||||
uword[MAX_NUM_CIRCLES] circle_x
|
||||
uword[MAX_NUM_CIRCLES] circle_y
|
||||
ubyte[MAX_NUM_CIRCLES] circle_radius
|
||||
ubyte color
|
||||
uword total_num_circles
|
||||
|
||||
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||
if use_kernal
|
||||
cx16.set_screen_mode(128)
|
||||
else
|
||||
gfx_lores.graphics_mode()
|
||||
|
||||
math.rndseed(12345,6789)
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
total_num_circles = 0
|
||||
color = 16
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
if use_kernal {
|
||||
cx16.GRAPH_set_colors(0,0,0)
|
||||
cx16.GRAPH_clear()
|
||||
}
|
||||
else
|
||||
gfx_lores.clear_screen(0)
|
||||
total_num_circles += draw_circles(use_kernal, max_time)
|
||||
}
|
||||
|
||||
if use_kernal
|
||||
cx16.set_screen_mode(3)
|
||||
else {
|
||||
gfx_lores.text_mode()
|
||||
}
|
||||
|
||||
return total_num_circles
|
||||
}
|
||||
|
||||
sub draw_circles(bool use_kernal, uword max_time) -> uword {
|
||||
uword @zp x
|
||||
uword @zp y
|
||||
ubyte @zp radius
|
||||
|
||||
ubyte num_circles
|
||||
|
||||
while num_circles<MAX_NUM_CIRCLES and cbm.RDTIM16()<max_time {
|
||||
x = math.rndw() % 320
|
||||
y = math.rndw() % 240
|
||||
radius = GROWTH_RATE
|
||||
if not_colliding() {
|
||||
while not_edge() and not_colliding() {
|
||||
radius += GROWTH_RATE
|
||||
}
|
||||
radius -= GROWTH_RATE
|
||||
if radius>0 {
|
||||
color++
|
||||
if color==0
|
||||
color=16
|
||||
if use_kernal {
|
||||
cx16.GRAPH_set_colors(color, 255-color, 0)
|
||||
cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true)
|
||||
}
|
||||
else
|
||||
gfx_lores.disc(x, y as ubyte, radius, color)
|
||||
circle_x[num_circles] = x
|
||||
circle_y[num_circles] = y
|
||||
circle_radius[num_circles] = radius
|
||||
num_circles++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num_circles
|
||||
|
||||
sub not_colliding() -> bool {
|
||||
if num_circles==0
|
||||
return true
|
||||
ubyte @zp c
|
||||
for c in 0 to num_circles-1 {
|
||||
if distance(c) < (radius as uword) + circle_radius[c]
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub distance(ubyte cix) -> uword {
|
||||
word dx = x as word - circle_x[cix]
|
||||
word dy = y as word - circle_y[cix]
|
||||
uword sqx = dx*dx as uword
|
||||
uword sqy = dy*dy as uword
|
||||
return sqrt(sqx + sqy)
|
||||
}
|
||||
|
||||
sub not_edge() -> bool {
|
||||
if x as word - radius < 0
|
||||
return false
|
||||
if x + radius >= 320
|
||||
return false
|
||||
if y as word - radius < 0
|
||||
return false
|
||||
if y + radius >= 240
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
123
benchmark-program/b_life.p8
Normal file
123
benchmark-program/b_life.p8
Normal file
@ -0,0 +1,123 @@
|
||||
; conway's game of life.
|
||||
|
||||
%import math
|
||||
%import textio
|
||||
|
||||
life {
|
||||
const ubyte WIDTH = 40
|
||||
const ubyte HEIGHT = 30
|
||||
const uword STRIDE = $0002+WIDTH
|
||||
uword world1 = memory("world1", (WIDTH+2)*(HEIGHT+2), 0)
|
||||
uword world2 = memory("world2", (WIDTH+2)*(HEIGHT+2), 0)
|
||||
uword @requirezp active_world = world1
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
txt.clear_screen()
|
||||
sys.memset(world1, (WIDTH+2)*(HEIGHT+2), 0)
|
||||
sys.memset(world2, (WIDTH+2)*(HEIGHT+2), 0)
|
||||
|
||||
set_start_gen()
|
||||
|
||||
uword gen
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
next_gen()
|
||||
gen++
|
||||
}
|
||||
|
||||
return gen
|
||||
}
|
||||
|
||||
sub set_start_gen() {
|
||||
|
||||
; some way to set a custom start generation:
|
||||
; str start_gen = " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " ** " +
|
||||
; " * * " +
|
||||
; " * " +
|
||||
; " * * " +
|
||||
; " ****** " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " " +
|
||||
; " "
|
||||
;
|
||||
; for y in 0 to 15 {
|
||||
; for x in 0 to 15 {
|
||||
; if start_gen[y*16 + x]=='*'
|
||||
; active_world[offset + x] = 1
|
||||
; }
|
||||
; offset += STRIDE
|
||||
; }
|
||||
|
||||
; randomize whole world
|
||||
math.rndseed(12345,9999)
|
||||
uword offset = STRIDE+1
|
||||
ubyte x
|
||||
ubyte y
|
||||
for y in 0 to HEIGHT-1 {
|
||||
for x in 0 to WIDTH-1 {
|
||||
active_world[offset+x] = math.rnd() & 1
|
||||
}
|
||||
offset += STRIDE
|
||||
}
|
||||
}
|
||||
|
||||
sub next_gen() {
|
||||
const ubyte DXOFFSET = 0
|
||||
const ubyte DYOFFSET = 0
|
||||
ubyte[2] cell_chars = [sc:' ', sc:'●']
|
||||
|
||||
uword @requirezp new_world = world1
|
||||
if active_world == world1
|
||||
new_world = world2
|
||||
|
||||
; To avoid re-calculating word index lookups into the new- and active world arrays,
|
||||
; we calculate the required pointer values upfront.
|
||||
; Inside the loop we can use ptr+x just fine (results in efficient LDA (ptr),Y instruction because x is a byte type),
|
||||
; and for each row we simply add the stride to the pointer.
|
||||
; It's more readable to use active_world[offset] etc, but offset is a word value, and this produces
|
||||
; inefficient assembly code because we can't use a register indexed mode in this case. Costly inside a loop.
|
||||
|
||||
uword @requirezp new_world_ptr = new_world + STRIDE+1-DXOFFSET
|
||||
uword @requirezp active_world_ptr = active_world + STRIDE+1-DXOFFSET
|
||||
|
||||
ubyte x
|
||||
ubyte y
|
||||
for y in DYOFFSET to HEIGHT+DYOFFSET-1 {
|
||||
|
||||
cx16.vaddr_autoincr(1, $b000 + 256*y, 0, 2) ; allows us to use simple Vera data byte assigns later instead of setchr() calls
|
||||
|
||||
for x in DXOFFSET to WIDTH+DXOFFSET-1 {
|
||||
; count the living neighbors
|
||||
ubyte cell = @(active_world_ptr + x)
|
||||
uword @requirezp ptr = active_world_ptr + x - STRIDE - 1
|
||||
ubyte neighbors = @(ptr) + @(ptr+1) + @(ptr+2) +
|
||||
@(ptr+STRIDE) + cell + @(ptr+STRIDE+2) +
|
||||
@(ptr+STRIDE*2) + @(ptr+STRIDE*2+1) + @(ptr+STRIDE*2+2)
|
||||
|
||||
; apply game of life rules
|
||||
if neighbors==3
|
||||
cell=1
|
||||
else if neighbors!=4
|
||||
cell=0
|
||||
@(new_world_ptr + x) = cell
|
||||
|
||||
; draw new cell
|
||||
; txt.setchr(x,y,cell_chars[cell])
|
||||
cx16.VERA_DATA0 = cell_chars[cell]
|
||||
}
|
||||
active_world_ptr += STRIDE
|
||||
new_world_ptr += STRIDE
|
||||
}
|
||||
|
||||
active_world = new_world
|
||||
}
|
||||
}
|
54
benchmark-program/b_mandelbrot.p8
Normal file
54
benchmark-program/b_mandelbrot.p8
Normal file
@ -0,0 +1,54 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
mandelbrot {
|
||||
const ubyte width = 39
|
||||
const ubyte height = 29
|
||||
const ubyte max_iter = 15
|
||||
|
||||
sub calc(uword max_time) -> uword {
|
||||
uword num_pixels
|
||||
ubyte pixelx
|
||||
ubyte pixely
|
||||
|
||||
txt.home()
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.RDTIM16() < max_time {
|
||||
for pixely in 0 to height-1 {
|
||||
float yy = (pixely as float)/0.40/height - 1.3
|
||||
|
||||
for pixelx in 0 to width-1 {
|
||||
float xx = (pixelx as float)/0.32/width - 2.2
|
||||
|
||||
float xsquared = 0.0
|
||||
float ysquared = 0.0
|
||||
float x = 0.0
|
||||
float y = 0.0
|
||||
ubyte iter = 0
|
||||
|
||||
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||
y = x*y*2.0 + yy
|
||||
x = xsquared - ysquared + xx
|
||||
xsquared = x*x
|
||||
ysquared = y*y
|
||||
iter++
|
||||
}
|
||||
txt.color2(1, max_iter-iter)
|
||||
txt.spc()
|
||||
num_pixels++
|
||||
|
||||
if cbm.RDTIM16()>=max_time
|
||||
goto finished
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
txt.clear_screen()
|
||||
}
|
||||
|
||||
finished:
|
||||
txt.color2(1, 6)
|
||||
return num_pixels
|
||||
}
|
||||
}
|
343
benchmark-program/b_maze.p8
Normal file
343
benchmark-program/b_maze.p8
Normal file
@ -0,0 +1,343 @@
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
; Even though prog8 only has support for extremely limited recursion,
|
||||
; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure.
|
||||
; This program shows a depth-first maze generation algorithm (1 possible path from start to finish),
|
||||
; and a depth-first maze solver algorithm, both using a stack to store the path taken.
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
maze {
|
||||
uword score
|
||||
|
||||
sub bench(uword max_time) -> uword {
|
||||
txt.nl()
|
||||
score=0
|
||||
math.rndseed(2345,44332)
|
||||
cbm.SETTIM(0,0,0)
|
||||
while cbm.RDTIM16()<max_time {
|
||||
maze.initialize()
|
||||
maze.drawStartFinish()
|
||||
if maze.generate(max_time) {
|
||||
maze.openpassages()
|
||||
maze.drawStartFinish()
|
||||
if maze.solve(max_time) {
|
||||
maze.drawStartFinish()
|
||||
} else break
|
||||
} else break
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
const uword screenwidth = 40
|
||||
const uword screenheight = 30
|
||||
|
||||
const ubyte numCellsHoriz = (screenwidth-1) / 2
|
||||
const ubyte numCellsVert = (screenheight-1) / 2
|
||||
|
||||
; maze start and finish cells
|
||||
const ubyte startCx = 0
|
||||
const ubyte startCy = 0
|
||||
const ubyte finishCx = numCellsHoriz-1
|
||||
const ubyte finishCy = numCellsVert-1
|
||||
|
||||
; cell properties
|
||||
const ubyte STONE = 128
|
||||
const ubyte WALKED = 64
|
||||
const ubyte BACKTRACKED = 32
|
||||
const ubyte UP = 1
|
||||
const ubyte RIGHT = 2
|
||||
const ubyte DOWN = 4
|
||||
const ubyte LEFT = 8
|
||||
const ubyte WALLCOLOR = 12
|
||||
const ubyte EMPTYCOLOR = 0
|
||||
|
||||
; unfortunately on larger screens (cx16), the number of cells exceeds 256 and doesn't fit in a regular array anymore.
|
||||
uword cells = memory("cells", numCellsHoriz*numCellsVert, 0)
|
||||
|
||||
ubyte[256] cx_stack
|
||||
ubyte[256] cy_stack
|
||||
ubyte stackptr
|
||||
|
||||
ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN]
|
||||
|
||||
sub generate(uword max_time) -> bool {
|
||||
ubyte cx = startCx
|
||||
ubyte cy = startCy
|
||||
|
||||
stackptr = 0
|
||||
@(celladdr(cx,cy)) &= ~STONE
|
||||
drawCell(cx, cy)
|
||||
uword cells_to_carve = numCellsHoriz * numCellsVert - 1
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
carve_restart_after_repath:
|
||||
ubyte direction = choose_uncarved_direction()
|
||||
if direction==0 {
|
||||
;backtrack
|
||||
stackptr--
|
||||
if stackptr==255 {
|
||||
; stack empty.
|
||||
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
|
||||
if cells_to_carve!=0 {
|
||||
if repath()
|
||||
goto carve_restart_after_repath
|
||||
}
|
||||
return true
|
||||
}
|
||||
cx = cx_stack[stackptr]
|
||||
cy = cy_stack[stackptr]
|
||||
} else {
|
||||
cx_stack[stackptr] = cx
|
||||
cy_stack[stackptr] = cy
|
||||
stackptr++
|
||||
if stackptr==0 {
|
||||
; stack overflow, we can't track our path any longer.
|
||||
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
|
||||
if cells_to_carve!=0 {
|
||||
if repath()
|
||||
goto carve_restart_after_repath
|
||||
}
|
||||
return true
|
||||
}
|
||||
@(celladdr(cx,cy)) |= direction
|
||||
when direction {
|
||||
UP -> {
|
||||
cy--
|
||||
@(celladdr(cx,cy)) |= DOWN
|
||||
}
|
||||
RIGHT -> {
|
||||
cx++
|
||||
@(celladdr(cx,cy)) |= LEFT
|
||||
|
||||
score++
|
||||
}
|
||||
DOWN -> {
|
||||
cy++
|
||||
@(celladdr(cx,cy)) |= UP
|
||||
}
|
||||
LEFT -> {
|
||||
cx--
|
||||
@(celladdr(cx,cy)) |= RIGHT
|
||||
}
|
||||
}
|
||||
@(celladdr(cx,cy)) &= ~STONE
|
||||
cells_to_carve--
|
||||
drawCell(cx, cy)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
sub repath() -> bool {
|
||||
; repath: try to find a new start cell with possible directions.
|
||||
; we limit our number of searches so that the algorith doesn't get stuck
|
||||
; for too long on bad rng... just accept a few unused cells in that case.
|
||||
repeat 255 {
|
||||
do {
|
||||
cx = math.rnd() % numCellsHoriz
|
||||
cy = math.rnd() % numCellsVert
|
||||
} until @(celladdr(cx, cy)) & STONE ==0
|
||||
if available_uncarved()!=0
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub available_uncarved() -> ubyte {
|
||||
ubyte candidates = 0
|
||||
if cx>0 and @(celladdr(cx-1, cy)) & STONE !=0
|
||||
candidates |= LEFT
|
||||
if cx<numCellsHoriz-1 and @(celladdr(cx+1, cy)) & STONE !=0
|
||||
candidates |= RIGHT
|
||||
if cy>0 and @(celladdr(cx, cy-1)) & STONE !=0
|
||||
candidates |= UP
|
||||
if cy<numCellsVert-1 and @(celladdr(cx, cy+1)) & STONE !=0
|
||||
candidates |= DOWN
|
||||
return candidates
|
||||
}
|
||||
|
||||
sub choose_uncarved_direction() -> ubyte {
|
||||
ubyte candidates = available_uncarved()
|
||||
if candidates==0
|
||||
return 0
|
||||
|
||||
repeat {
|
||||
ubyte choice = candidates & directionflags[math.rnd() & 3]
|
||||
if choice!=0
|
||||
return choice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub openpassages() {
|
||||
; open just a few extra passages, so that multiple routes are possible in theory.
|
||||
ubyte numpassages
|
||||
ubyte cx
|
||||
ubyte cy
|
||||
do {
|
||||
do {
|
||||
cx = math.rnd() % (numCellsHoriz-2) + 1
|
||||
cy = math.rnd() % (numCellsVert-2) + 1
|
||||
} until @(celladdr(cx, cy)) & STONE ==0
|
||||
ubyte direction = directionflags[math.rnd() & 3]
|
||||
if @(celladdr(cx, cy)) & direction == 0 {
|
||||
when direction {
|
||||
LEFT -> {
|
||||
if @(celladdr(cx-1,cy)) & STONE == 0 {
|
||||
@(celladdr(cx,cy)) |= LEFT
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
RIGHT -> {
|
||||
if @(celladdr(cx+1,cy)) & STONE == 0 {
|
||||
@(celladdr(cx,cy)) |= RIGHT
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
UP -> {
|
||||
if @(celladdr(cx,cy-1)) & STONE == 0 {
|
||||
@(celladdr(cx,cy)) |= UP
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
DOWN -> {
|
||||
if @(celladdr(cx,cy+1)) & STONE == 0 {
|
||||
@(celladdr(cx,cy)) |= DOWN
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} until numpassages==10
|
||||
}
|
||||
|
||||
sub solve(uword max_time) -> bool {
|
||||
ubyte cx = startCx
|
||||
ubyte cy = startCy
|
||||
const uword max_path_length = 1024
|
||||
|
||||
; the path through the maze can be longer than 256 so doesn't fit in a regular array.... :(
|
||||
uword pathstack = memory("pathstack", max_path_length, 0)
|
||||
uword pathstackptr = 0
|
||||
|
||||
@(celladdr(cx,cy)) |= WALKED
|
||||
; txt.setcc(cx*2+1, cy*2+1, 81, 1)
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
solve_loop:
|
||||
if cx==finishCx and cy==finishCy {
|
||||
;txt.home()
|
||||
txt.print("found! path length: ")
|
||||
txt.print_uw(pathstackptr)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
ubyte cell = @(celladdr(cx,cy))
|
||||
if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 {
|
||||
@(pathstack + pathstackptr) = UP
|
||||
;txt.setcc(cx*2+1, cy*2, 81, 3)
|
||||
cy--
|
||||
}
|
||||
else if cell & DOWN !=0 and @(celladdr(cx,cy+1)) & (WALKED|BACKTRACKED) ==0 {
|
||||
@(pathstack + pathstackptr) = DOWN
|
||||
;txt.setcc(cx*2+1, cy*2+2, 81, 3)
|
||||
cy++
|
||||
}
|
||||
else if cell & LEFT !=0 and @(celladdr(cx-1,cy)) & (WALKED|BACKTRACKED) ==0 {
|
||||
@(pathstack + pathstackptr) = LEFT
|
||||
;txt.setcc(cx*2, cy*2+1, 81, 3)
|
||||
cx--
|
||||
}
|
||||
else if cell & RIGHT !=0 and @(celladdr(cx+1,cy)) & (WALKED|BACKTRACKED) ==0 {
|
||||
@(pathstack + pathstackptr) = RIGHT
|
||||
;txt.setcc(cx*2+2, cy*2+1, 81, 3)
|
||||
cx++
|
||||
}
|
||||
else {
|
||||
; dead end, pop stack
|
||||
pathstackptr--
|
||||
if pathstackptr==65535 {
|
||||
txt.print("no solution?!\n")
|
||||
return true
|
||||
}
|
||||
@(celladdr(cx,cy)) |= BACKTRACKED
|
||||
;txt.setcc(cx*2+1, cy*2+1, 81, 2)
|
||||
when @(pathstack + pathstackptr) {
|
||||
UP -> {
|
||||
;txt.setcc(cx*2+1, cy*2+2, 81, 9)
|
||||
cy++
|
||||
}
|
||||
DOWN -> {
|
||||
;txt.setcc(cx*2+1, cy*2, 81, 9)
|
||||
cy--
|
||||
}
|
||||
LEFT -> {
|
||||
;txt.setcc(cx*2+2, cy*2+1, 81, 9)
|
||||
cx++
|
||||
}
|
||||
RIGHT -> {
|
||||
;txt.setcc(cx*2, cy*2+1, 81, 9)
|
||||
cx--
|
||||
|
||||
score++
|
||||
}
|
||||
}
|
||||
goto solve_loop
|
||||
}
|
||||
pathstackptr++
|
||||
if pathstackptr==max_path_length {
|
||||
txt.print("stack overflow, path too long\n")
|
||||
return true
|
||||
}
|
||||
@(celladdr(cx,cy)) |= WALKED
|
||||
;txt.setcc(cx*2+1, cy*2+1, 81, 1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub celladdr(ubyte cx, ubyte cy) -> uword {
|
||||
return cells+(numCellsHoriz as uword)*cy+cx
|
||||
}
|
||||
|
||||
sub drawCell(ubyte cx, ubyte cy) {
|
||||
return
|
||||
; ubyte x = cx * 2 + 1
|
||||
; ubyte y = cy * 2 + 1
|
||||
; ubyte doors = @(celladdr(cx,cy))
|
||||
; if doors & UP !=0
|
||||
; txt.setcc(x, y-1, ' ', EMPTYCOLOR)
|
||||
; if doors & RIGHT !=0
|
||||
; txt.setcc(x+1, y, ' ', EMPTYCOLOR)
|
||||
; if doors & DOWN !=0
|
||||
; txt.setcc(x, y+1, ' ', EMPTYCOLOR)
|
||||
; if doors & LEFT !=0
|
||||
; txt.setcc(x-1, y, ' ', EMPTYCOLOR)
|
||||
; if doors & STONE !=0
|
||||
; txt.setcc(x, y, 160, WALLCOLOR)
|
||||
; else
|
||||
; txt.setcc(x, y, 32, EMPTYCOLOR)
|
||||
;
|
||||
; if doors & WALKED !=0
|
||||
; txt.setcc(x, y, 81, 1)
|
||||
; if doors & BACKTRACKED !=0
|
||||
; txt.setcc(x, y, 81, 2)
|
||||
}
|
||||
|
||||
sub initialize() {
|
||||
sys.memset(cells, numCellsHoriz*numCellsVert, STONE)
|
||||
; txt.fill_screen(160, WALLCOLOR)
|
||||
drawStartFinish()
|
||||
}
|
||||
|
||||
sub drawStartFinish() {
|
||||
; txt.setcc(startCx*2+1,startCy*2+1,sc:'s',5)
|
||||
; txt.setcc(finishCx*2+1, finishCy*2+1, sc:'f', 13)
|
||||
}
|
||||
}
|
63
benchmark-program/b_queens.p8
Normal file
63
benchmark-program/b_queens.p8
Normal file
@ -0,0 +1,63 @@
|
||||
%import textio
|
||||
|
||||
; Recursive N-Queens solver.
|
||||
; The problem is: find all possible ways to place 8 Queen chess pieces on a chess board, so that none of them attacks any other.
|
||||
; (this program prints all solutions without taking mirroring and flipping the chess board into account)
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
queens {
|
||||
const ubyte NUMQUEENS=8
|
||||
ubyte[NUMQUEENS] board
|
||||
|
||||
sub could_place(ubyte row, ubyte col) -> bool {
|
||||
if row==0
|
||||
return true
|
||||
ubyte i
|
||||
for i in 0 to row-1 {
|
||||
if board[i]==col or board[i]-i==col-row or board[i]+i==col+row
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
uword solution_count
|
||||
uword maximum_duration
|
||||
|
||||
sub place_queen(ubyte row) -> bool {
|
||||
if row == NUMQUEENS {
|
||||
solution_count++
|
||||
txt.chrout('.')
|
||||
return cbm.RDTIM16()<maximum_duration
|
||||
}
|
||||
bool continue_running=true
|
||||
ubyte col
|
||||
for col in 0 to NUMQUEENS-1 {
|
||||
if could_place(row, col) {
|
||||
board[row] = col
|
||||
; we need to save the local variables row and col.
|
||||
sys.push(row)
|
||||
sys.push(col)
|
||||
continue_running = place_queen(row + 1)
|
||||
; restore the local variables after the recursive call.
|
||||
col = sys.pop()
|
||||
row = sys.pop()
|
||||
board[row] = 0
|
||||
|
||||
if not continue_running
|
||||
break
|
||||
}
|
||||
}
|
||||
return continue_running
|
||||
}
|
||||
|
||||
sub bench(uword max_time) -> uword {
|
||||
solution_count = 0
|
||||
maximum_duration = max_time
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
while cbm.RDTIM16() < maximum_duration {
|
||||
void place_queen(0)
|
||||
}
|
||||
return solution_count
|
||||
}
|
||||
}
|
69
benchmark-program/b_sprites.p8
Normal file
69
benchmark-program/b_sprites.p8
Normal file
@ -0,0 +1,69 @@
|
||||
%import sprites
|
||||
%import coroutines
|
||||
%import math
|
||||
|
||||
|
||||
animsprites {
|
||||
uword num_iterations
|
||||
ubyte[64] sx
|
||||
ubyte[64] sy
|
||||
ubyte[64] sc
|
||||
ubyte[64] dx
|
||||
ubyte[64] dy
|
||||
uword maximum_duration
|
||||
|
||||
sub benchmark(uword max_duration) -> uword {
|
||||
maximum_duration = max_duration
|
||||
math.rndseed(1122,9876)
|
||||
cx16.set_screen_mode(3)
|
||||
cx16.mouse_config2(1)
|
||||
sprites.set_mousepointer_hand()
|
||||
repeat 64
|
||||
void coroutines.add(animsprite, 0)
|
||||
cx16.mouse_config2(0)
|
||||
|
||||
cbm.SETTIM(0,0,0)
|
||||
coroutines.run(supervisor)
|
||||
|
||||
sprites.reset(0, 64)
|
||||
return num_iterations
|
||||
}
|
||||
|
||||
sub supervisor() -> bool {
|
||||
if cbm.RDTIM16() >= maximum_duration {
|
||||
coroutines.killall()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub animsprite() {
|
||||
num_iterations++
|
||||
; set up the sprite
|
||||
ubyte sprnum = coroutines.current()
|
||||
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||
sx[sprnum] = math.rnd()
|
||||
sy[sprnum] = math.rnd()
|
||||
sc[sprnum] = math.rnd()
|
||||
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
|
||||
; move the sprite around
|
||||
while sc[sprnum]!=0 {
|
||||
animate(sprnum)
|
||||
void coroutines.yield()
|
||||
sprnum = coroutines.current()
|
||||
}
|
||||
|
||||
sub animate(ubyte spr) {
|
||||
defer sc[spr]--
|
||||
sprites.pos(spr, sx[spr], sy[spr])
|
||||
sx[spr] += dx[spr]
|
||||
sy[spr] += dy[spr]
|
||||
}
|
||||
|
||||
; end the task but replace it with a fresh animated sprite task
|
||||
void coroutines.add(animsprite, 0)
|
||||
}
|
||||
}
|
989
benchmark-program/b_textelite.p8
Normal file
989
benchmark-program/b_textelite.p8
Normal file
@ -0,0 +1,989 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import strings
|
||||
|
||||
|
||||
textelite {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
uword num_commands
|
||||
|
||||
sub bench(uword max_time) -> uword {
|
||||
num_commands = 0
|
||||
txt.lowercase()
|
||||
cbm.SETTIM(0,0,0)
|
||||
while cbm.RDTIM16()<max_time {
|
||||
reinit()
|
||||
run_commands(max_time)
|
||||
}
|
||||
return num_commands
|
||||
}
|
||||
|
||||
sub reinit() {
|
||||
;txt.clear_screen()
|
||||
;txt.print("\n --- TextElite v1.3 ---\n")
|
||||
txt.print("\nnew game\n")
|
||||
elite_planet.set_seed(0, 0)
|
||||
elite_galaxy.travel_to(1, numforLave)
|
||||
elite_market.init(0) ; Lave's market is seeded with 0
|
||||
elite_ship.init()
|
||||
elite_planet.display(false, 0)
|
||||
input_index = 0
|
||||
}
|
||||
|
||||
sub run_commands(uword max_time) {
|
||||
while cbm.RDTIM16() < max_time {
|
||||
str input = "????????"
|
||||
;txt.print("\nCash: ")
|
||||
;elite_util.print_10s(elite_ship.cash)
|
||||
;txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = next_input(input)
|
||||
;txt.nl()
|
||||
if num_chars!=0 {
|
||||
when input[0] {
|
||||
'q' -> {
|
||||
bool has_error = false
|
||||
if elite_galaxy.number != 2 {
|
||||
txt.print("\nERROR: galaxy is not 2: ")
|
||||
txt.print_ub(elite_galaxy.number)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_planet.number != 164 {
|
||||
txt.print("\nERROR: planet is not 164: ")
|
||||
txt.print_ub(elite_planet.number)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_planet.x != 116 {
|
||||
txt.print("\nERROR: planet.x is not 116: ")
|
||||
txt.print_ub(elite_planet.x)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_planet.y != 201 {
|
||||
txt.print("\nERROR: planet.y is not 201: ")
|
||||
txt.print_ub(elite_planet.y)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if "ribeen" != elite_planet.name {
|
||||
txt.print("\nERROR: planet.name is not 'ribeen': ")
|
||||
txt.print(elite_planet.name)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_ship.cash != 1212 {
|
||||
txt.print("\nERROR: cash is not 1212: ")
|
||||
txt.print_uw(elite_ship.cash)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_ship.fuel != 50 {
|
||||
txt.print("\nERROR: fuel is not 50:")
|
||||
txt.print_ub(elite_ship.fuel)
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_ship.cargohold[0] != 3 {
|
||||
txt.print("\nERROR: food is not 3:")
|
||||
txt.print_ub(elite_ship.cargohold[0])
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if elite_ship.cargohold[1] != 0 {
|
||||
txt.print("\nERROR: textiles is not 0:")
|
||||
txt.print_ub(elite_ship.cargohold[1])
|
||||
txt.nl()
|
||||
has_error=true
|
||||
}
|
||||
if has_error
|
||||
sys.exit(1)
|
||||
return
|
||||
}
|
||||
'b' -> elite_trader.do_buy()
|
||||
's' -> elite_trader.do_sell()
|
||||
'f' -> elite_trader.do_fuel()
|
||||
'j' -> elite_trader.do_jump()
|
||||
't' -> elite_trader.do_teleport()
|
||||
'g' -> elite_trader.do_next_galaxy()
|
||||
'i' -> elite_trader.do_info()
|
||||
'm' -> {
|
||||
if input[1]=='a' and input[2]=='p'
|
||||
elite_trader.do_map()
|
||||
else
|
||||
elite_trader.do_show_market()
|
||||
}
|
||||
'l' -> elite_trader.do_local()
|
||||
'c' -> elite_trader.do_cash()
|
||||
'h' -> elite_trader.do_hold()
|
||||
}
|
||||
num_commands++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str[] inputs = [
|
||||
"i",
|
||||
"diso",
|
||||
"i",
|
||||
"lave",
|
||||
"m",
|
||||
"b",
|
||||
"food",
|
||||
"15",
|
||||
"map",
|
||||
"g",
|
||||
"map",
|
||||
"l",
|
||||
"j",
|
||||
"zao",
|
||||
"s",
|
||||
"food",
|
||||
"12",
|
||||
"tele",
|
||||
"quti",
|
||||
"tele",
|
||||
"aro",
|
||||
"i",
|
||||
"diso",
|
||||
"i",
|
||||
"lave",
|
||||
"i",
|
||||
"zao",
|
||||
"galhyp",
|
||||
"fuel",
|
||||
"20",
|
||||
"j",
|
||||
"rib",
|
||||
"i",
|
||||
"rib",
|
||||
"i",
|
||||
"tiri",
|
||||
"q",
|
||||
0
|
||||
]
|
||||
|
||||
ubyte input_index
|
||||
|
||||
sub next_input(str buffer) -> ubyte {
|
||||
input_index++
|
||||
return strings.copy(inputs[input_index], buffer)
|
||||
}
|
||||
}
|
||||
|
||||
elite_trader {
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
sub do_jump() {
|
||||
;txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
;txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = elite_ship.fuel
|
||||
elite_ship.fuel = 255
|
||||
jump_to_system()
|
||||
elite_ship.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void textelite.next_input(input)
|
||||
ubyte current_planet = elite_planet.number
|
||||
ubyte x = elite_planet.x
|
||||
ubyte y = elite_planet.y
|
||||
if elite_galaxy.search_closest_planet(input) {
|
||||
ubyte distance = elite_planet.distance(x, y)
|
||||
if distance <= elite_ship.fuel {
|
||||
elite_galaxy.init_market_for_planet()
|
||||
elite_ship.fuel -= distance
|
||||
;txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
elite_planet.display(true,0 )
|
||||
return
|
||||
}
|
||||
;txt.print("\nInsufficient fuel\n")
|
||||
} else {
|
||||
;txt.print(" Not found!\n")
|
||||
}
|
||||
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
;txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void textelite.next_input(commodity)
|
||||
ubyte ci = elite_market.match(commodity)
|
||||
if ci & 128 !=0 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
;txt.print("\nHow much? ")
|
||||
void textelite.next_input(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if elite_market.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = elite_market.current_price[ci] * amount
|
||||
;txt.print(" Total price: ")
|
||||
;elite_util.print_10s(price)
|
||||
if price > elite_ship.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
elite_ship.cash -= price
|
||||
elite_ship.cargohold[ci] += amount
|
||||
elite_market.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
;txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void textelite.next_input(commodity)
|
||||
ubyte ci = elite_market.match(commodity)
|
||||
if ci & 128 !=0 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
;txt.print("\nHow much? ")
|
||||
void textelite.next_input(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if elite_ship.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = elite_market.current_price[ci] * amount
|
||||
;txt.print(" Total price: ")
|
||||
;elite_util.print_10s(price)
|
||||
elite_ship.cash += price
|
||||
elite_ship.cargohold[ci] -= amount
|
||||
elite_market.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
;txt.print("\nBuy fuel. Amount? ")
|
||||
void textelite.next_input(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = elite_ship.Max_fuel - elite_ship.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * elite_ship.Fuel_cost
|
||||
if price > elite_ship.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
elite_ship.cash -= price
|
||||
elite_ship.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
;txt.print("\nCheat! Set cash amount: ")
|
||||
void textelite.next_input(input)
|
||||
elite_ship.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
;txt.print("\nCheat! Set cargohold size: ")
|
||||
void textelite.next_input(input)
|
||||
elite_ship.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy() {
|
||||
txt.print("\n>>>>> Galaxy Hyperjump!\n")
|
||||
elite_galaxy.travel_to(elite_galaxy.number+1, elite_planet.number)
|
||||
elite_planet.display(false, 0)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
;txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = textelite.next_input(input)
|
||||
if num_chars!=0 {
|
||||
ubyte current_planet = elite_planet.number
|
||||
ubyte x = elite_planet.x
|
||||
ubyte y = elite_planet.y
|
||||
if elite_galaxy.search_closest_planet(input) {
|
||||
ubyte distance = elite_planet.distance(x, y)
|
||||
elite_planet.display(false, distance)
|
||||
} else {
|
||||
;txt.print(" Not found!")
|
||||
}
|
||||
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
|
||||
} else {
|
||||
elite_planet.display(false, 0)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
elite_galaxy.local_area()
|
||||
}
|
||||
|
||||
sub do_map() {
|
||||
;txt.print("\n(l)ocal or (g)alaxy starmap? ")
|
||||
num_chars = textelite.next_input(input)
|
||||
if num_chars!=0 {
|
||||
elite_galaxy.starmap(input[0]=='l')
|
||||
}
|
||||
}
|
||||
|
||||
sub do_show_market() {
|
||||
elite_market.display()
|
||||
;txt.print("\nFuel: ")
|
||||
;elite_util.print_10s(elite_ship.fuel)
|
||||
;txt.print(" Cargohold space: ")
|
||||
;txt.print_ub(elite_ship.cargo_free())
|
||||
;txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
elite_ship {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel
|
||||
uword cash
|
||||
ubyte[17] cargohold
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
fuel = Max_fuel
|
||||
cash = 1000
|
||||
}
|
||||
}
|
||||
|
||||
elite_market {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity
|
||||
uword[17] current_price
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all commands are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = elite_planet.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80 !=0
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
return
|
||||
; ubyte ci
|
||||
; txt.nl()
|
||||
; elite_planet.print_name_uppercase()
|
||||
; txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
; for ci in 0 to len(names)-1 {
|
||||
; elite_util.print_right(13, names[ci])
|
||||
; txt.print(" ")
|
||||
; elite_util.print_10s(current_price[ci])
|
||||
; txt.column(24)
|
||||
; txt.print_ub(current_quantity[ci])
|
||||
; txt.chrout(' ')
|
||||
; when units[ci] {
|
||||
; 0 -> txt.chrout('t')
|
||||
; 1 -> txt.print("kg")
|
||||
; 2 -> txt.chrout('g')
|
||||
; }
|
||||
; txt.column(32)
|
||||
; txt.print_ub(elite_ship.cargohold[ci])
|
||||
; txt.nl()
|
||||
; }
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if elite_util.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
elite_galaxy {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxynum) {
|
||||
number = 1
|
||||
elite_planet.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxynum-1 {
|
||||
nextgalaxy()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy() {
|
||||
textelite.num_commands++
|
||||
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxynum, ubyte system) {
|
||||
init(galaxynum)
|
||||
generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet()
|
||||
textelite.num_commands++
|
||||
}
|
||||
elite_planet.name = make_current_planet_name()
|
||||
init_market_for_planet()
|
||||
}
|
||||
|
||||
sub init_market_for_planet() {
|
||||
elite_market.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet(uword nameptr) -> bool {
|
||||
textelite.num_commands++
|
||||
|
||||
ubyte x = elite_planet.x
|
||||
ubyte y = elite_planet.y
|
||||
ubyte current_planet_num = elite_planet.number
|
||||
|
||||
init(number)
|
||||
bool found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet()
|
||||
elite_planet.name = make_current_planet_name()
|
||||
if elite_util.prefix_matches(nameptr, elite_planet.name) {
|
||||
ubyte distance = elite_planet.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet = elite_planet.number
|
||||
ubyte px = elite_planet.x
|
||||
ubyte py = elite_planet.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
; txt.print("\nGalaxy #")
|
||||
; txt.print_ub(number)
|
||||
; txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet()
|
||||
ubyte distance = elite_planet.distance(px, py)
|
||||
if distance <= elite_ship.Max_fuel {
|
||||
; if distance <= elite_ship.fuel
|
||||
; txt.chrout('*')
|
||||
; else
|
||||
; txt.chrout('-')
|
||||
; txt.spc()
|
||||
elite_planet.name = make_current_planet_name()
|
||||
elite_planet.display(true, distance)
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet)
|
||||
}
|
||||
|
||||
sub starmap(bool local) {
|
||||
ubyte current_planet = elite_planet.number
|
||||
ubyte px = elite_planet.x
|
||||
ubyte py = elite_planet.y
|
||||
str current_name = " " ; 8 max
|
||||
ubyte pn = 0
|
||||
|
||||
current_name = elite_planet.name
|
||||
init(number)
|
||||
; txt.clear_screen()
|
||||
; txt.print("Galaxy #")
|
||||
; txt.print_ub(number)
|
||||
; if local
|
||||
; txt.print(" - local systems")
|
||||
; else
|
||||
; txt.print(" - galaxy")
|
||||
; txt.print(" starmap:\n")
|
||||
ubyte max_distance = 255
|
||||
if local
|
||||
max_distance = elite_ship.Max_fuel
|
||||
ubyte home_sx
|
||||
ubyte home_sy
|
||||
ubyte home_distance
|
||||
|
||||
do {
|
||||
generate_next_planet()
|
||||
ubyte distance = elite_planet.distance(px, py)
|
||||
if distance <= max_distance {
|
||||
elite_planet.name = make_current_planet_name()
|
||||
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
|
||||
uword tx = elite_planet.x
|
||||
uword ty = elite_planet.y
|
||||
if local {
|
||||
tx = tx + 24 - px
|
||||
ty = ty + 24 - py
|
||||
}
|
||||
ubyte sx = display_scale_x(tx)
|
||||
ubyte sy = display_scale_y(ty)
|
||||
ubyte char = '*'
|
||||
if elite_planet.number==current_planet
|
||||
char = '%'
|
||||
if local {
|
||||
print_planet_details(elite_planet.name, sx, sy, distance)
|
||||
} else if elite_planet.number==current_planet {
|
||||
home_distance = distance
|
||||
home_sx = sx
|
||||
home_sy = sy
|
||||
}
|
||||
; txt.setchr(2+sx, 2+sy, char)
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
if not local
|
||||
print_planet_details(current_name, home_sx, home_sy, home_distance)
|
||||
|
||||
; if local
|
||||
; txt.plot(0, display_scale_y(64) + 4)
|
||||
; else
|
||||
; txt.plot(0, display_scale_y(256) + 4 as ubyte)
|
||||
travel_to(number, current_planet)
|
||||
|
||||
sub print_planet_details(str name, ubyte screenx, ubyte screeny, ubyte d) {
|
||||
return
|
||||
; txt.plot(2+screenx-2, 2+screeny+1)
|
||||
; txt.print(name)
|
||||
; if d!=0 {
|
||||
; txt.plot(2+screenx-2, 2+screeny+2)
|
||||
; elite_util.print_10s(d)
|
||||
; txt.print(" LY")
|
||||
; }
|
||||
}
|
||||
|
||||
sub display_scale_x(uword x) -> ubyte {
|
||||
if local
|
||||
return x/2 as ubyte
|
||||
return x/8 as ubyte
|
||||
}
|
||||
|
||||
sub display_scale_y(uword y) -> ubyte {
|
||||
if local
|
||||
return y/4 as ubyte
|
||||
return y/16 as ubyte
|
||||
}
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
bool longname
|
||||
|
||||
sub generate_next_planet() {
|
||||
determine_planet_properties()
|
||||
longname = lsb(seed[0]) & 64 !=0
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet_properties() {
|
||||
; create the planet's characteristics
|
||||
elite_planet.number++
|
||||
elite_planet.x = msb(seed[1])
|
||||
elite_planet.y = msb(seed[0])
|
||||
elite_planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
elite_planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if elite_planet.govtype <= 1
|
||||
elite_planet.economy = (elite_planet.economy | 2)
|
||||
elite_planet.techlevel = (msb(seed[1]) & 3) + (elite_planet.economy ^ 7)
|
||||
elite_planet.techlevel += elite_planet.govtype >> 1
|
||||
if elite_planet.govtype & 1 !=0
|
||||
elite_planet.techlevel++
|
||||
elite_planet.population = 4 * elite_planet.techlevel + elite_planet.economy
|
||||
elite_planet.population += elite_planet.govtype + 1
|
||||
elite_planet.productivity = ((elite_planet.economy ^ 7) + 3) * (elite_planet.govtype + 4)
|
||||
elite_planet.productivity *= elite_planet.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
elite_planet.radius = mkword((seed2_msb & 15) + 11, elite_planet.x)
|
||||
elite_planet.species_is_alien = lsb(seed[2]) & 128 !=0 ; bit 7 of w2_lo
|
||||
if elite_planet.species_is_alien {
|
||||
elite_planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
elite_planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
elite_planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
elite_planet.species_kind = (elite_planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||
}
|
||||
|
||||
elite_planet.goatsoup_seed[0] = lsb(seed[1])
|
||||
elite_planet.goatsoup_seed[1] = msb(seed[1])
|
||||
elite_planet.goatsoup_seed[2] = lsb(seed[2])
|
||||
elite_planet.goatsoup_seed[3] = seed2_msb
|
||||
}
|
||||
|
||||
sub tweakseed() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
xh <<= 1 ; make sure carry flag is not used on first shift!
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
}
|
||||
|
||||
elite_planet {
|
||||
str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
|
||||
str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"]
|
||||
str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] @shared wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
bool species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str randname = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte xx = goatsoup_rnd_number() & $3e
|
||||
if pairs0[xx] != '.' {
|
||||
randname[nx] = pairs0[xx]
|
||||
nx++
|
||||
}
|
||||
xx++
|
||||
if pairs0[xx] != '.' {
|
||||
randname[nx] = pairs0[xx]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
randname[nx] = 0
|
||||
randname[0] = strings.upperchar(randname[0])
|
||||
return randname
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte xx = goatsoup_rnd[0] * 2
|
||||
uword a = xx as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = xx
|
||||
xx = goatsoup_rnd[1]
|
||||
ubyte ac = xx + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = xx
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = ((rnr >= $33) as ubyte) + ((rnr >= $66) as ubyte) + ((rnr >= $99) as ubyte) + ((rnr >= $CC) as ubyte)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = strings.upperchar(name[0])
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = strings.upperchar(name[0])
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc in ['e', 'o', 0]
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(bool compressed, ubyte distance) {
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub getword(ubyte listnum, ubyte wordidx) -> uword {
|
||||
uword list = wordlists[listnum-$81]
|
||||
return peekw(list + wordidx*2)
|
||||
}
|
||||
}
|
||||
|
||||
elite_util {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> bool {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
if strings.lowerchar(pc)!=strings.lowerchar(sc)
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
}
|
||||
}
|
117
benchmark-program/benchmark.p8
Normal file
117
benchmark-program/benchmark.p8
Normal file
@ -0,0 +1,117 @@
|
||||
|
||||
; This benchmark program is meant to check for regressions in the
|
||||
; Prog8 compiler's code-generator (performance wise).
|
||||
;
|
||||
; As the X16 computer is a more or less fixed system, it's not very useful
|
||||
; to benchmark the computer itself with.
|
||||
|
||||
|
||||
%import textio
|
||||
%import b_adpcm
|
||||
%import b_circles
|
||||
%import b_3d
|
||||
%import b_life
|
||||
%import b_mandelbrot
|
||||
%import b_queens
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
|
||||
main {
|
||||
|
||||
str[20] benchmark_names
|
||||
uword[20] benchmark_score
|
||||
|
||||
|
||||
sub start() {
|
||||
ubyte benchmark_number
|
||||
|
||||
cx16.set_screen_mode(3)
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
txt.print("\n\n\n prog8 compiler benchmark tests.\n")
|
||||
sys.wait(60)
|
||||
|
||||
benchmark_number = 0
|
||||
|
||||
announce_benchmark("maze solver")
|
||||
benchmark_score[benchmark_number] = maze.bench(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("n-queens")
|
||||
benchmark_score[benchmark_number] = queens.bench(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("mandelbrot (floating point)")
|
||||
benchmark_score[benchmark_number] = mandelbrot.calc(400)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("game of life")
|
||||
benchmark_score[benchmark_number] = life.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("3d model rotation")
|
||||
benchmark_score[benchmark_number] = rotate3d.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("adpcm audio decoding")
|
||||
benchmark_score[benchmark_number] = adpcm.decode_benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("circles with gfx_lores")
|
||||
benchmark_score[benchmark_number] = circles.draw(false, 300)
|
||||
benchmark_number++
|
||||
|
||||
; announce_benchmark("circles with kernal")
|
||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||
; benchmark_number++
|
||||
|
||||
announce_benchmark("text-elite")
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("sprites-coroutines-defer")
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
benchmark_number = 0
|
||||
txt.print("\nscore benchmark\n\n")
|
||||
do {
|
||||
txt.spc()
|
||||
txt.print_uw(benchmark_score[benchmark_number])
|
||||
txt.column(6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
final_score += benchmark_score[benchmark_number]
|
||||
txt.nl()
|
||||
benchmark_number++
|
||||
} until benchmark_names[benchmark_number]==0
|
||||
|
||||
txt.print("\n\nfinal score : ")
|
||||
txt.print_uw(final_score)
|
||||
txt.nl()
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
txt.plot(4, 6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
txt.nl()
|
||||
sys.wait(60)
|
||||
}
|
||||
}
|
||||
}
|
10
build.gradle
10
build.gradle
@ -1,10 +0,0 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
34
build.gradle.kts
Normal file
34
build.gradle.kts
Normal file
@ -0,0 +1,34 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.20"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply(plugin="kotlin")
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
// enable language features like so:
|
||||
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
24
codeCore/build.gradle.kts
Normal file
24
codeCore/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir("${project.projectDir}/src")
|
||||
}
|
||||
resources {
|
||||
srcDir("${project.projectDir}/res")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
31
codeCore/src/prog8/code/Globals.kt
Normal file
31
codeCore/src/prog8/code/Globals.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package prog8.code
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// the automatically generated module where all string literals are interned to:
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given path,
|
||||
* where links are replaced by the actual directories,
|
||||
* and containing no redundant path elements.
|
||||
* If the path doesn't refer to an existing directory or file on the file system,
|
||||
* it is returned unchanged.
|
||||
*/
|
||||
fun Path.sanitize(): Path {
|
||||
return try {
|
||||
this.toRealPath().normalize()
|
||||
} catch (_: java.nio.file.NoSuchFileException) {
|
||||
this.absolute().normalize()
|
||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
@ -1,258 +0,0 @@
|
||||
package prog8.code
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
/**
|
||||
* Tree structure containing all symbol definitions in the program
|
||||
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||
*/
|
||||
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
||||
fun print() = printIndented(0)
|
||||
|
||||
override fun printProperties() { }
|
||||
|
||||
/**
|
||||
* The table as a flat mapping of scoped names to the StNode.
|
||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||
*/
|
||||
val flat: Map<List<String>, StNode> by lazy {
|
||||
val result = mutableMapOf<List<String>, StNode>()
|
||||
fun flatten(node: StNode) {
|
||||
result[node.scopedName] = node
|
||||
node.children.values.forEach { flatten(it) }
|
||||
}
|
||||
children.values.forEach { flatten(it) }
|
||||
result
|
||||
}
|
||||
|
||||
val allVariables: Collection<StStaticVariable> by lazy {
|
||||
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 {
|
||||
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 {
|
||||
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: List<String>) = flat[scopedName]
|
||||
}
|
||||
|
||||
|
||||
enum class StNodeType {
|
||||
GLOBAL,
|
||||
// MODULE, // not used with current scoping rules
|
||||
BLOCK,
|
||||
SUBROUTINE,
|
||||
ROMSUB,
|
||||
LABEL,
|
||||
STATICVAR,
|
||||
MEMVAR,
|
||||
CONSTANT,
|
||||
BUILTINFUNC,
|
||||
MEMORYSLAB
|
||||
}
|
||||
|
||||
|
||||
open class StNode(val name: String,
|
||||
val type: StNodeType,
|
||||
val position: Position,
|
||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
lateinit var parent: StNode
|
||||
|
||||
val scopedName: List<String> by lazy {
|
||||
if(type== StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedName + name
|
||||
}
|
||||
|
||||
fun lookup(name: String) =
|
||||
lookupUnqualified(name)
|
||||
open fun lookup(scopedName: List<String>) =
|
||||
if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0])
|
||||
fun lookupOrElse(name: String, default: () -> StNode) =
|
||||
lookupUnqualified(name) ?: default()
|
||||
fun lookupOrElse(scopedName: List<String>, default: () -> StNode) =
|
||||
lookup(scopedName) ?: default()
|
||||
|
||||
private fun lookupQualified(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
|
||||
}
|
||||
|
||||
private fun lookupUnqualified(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 printIndented(indent: Int) {
|
||||
print(" ".repeat(indent))
|
||||
when(type) {
|
||||
StNodeType.GLOBAL -> print("SYMBOL-TABLE:")
|
||||
StNodeType.BLOCK -> print("(B) ")
|
||||
StNodeType.SUBROUTINE -> print("(S) ")
|
||||
StNodeType.LABEL -> print("(L) ")
|
||||
StNodeType.STATICVAR -> print("(V) ")
|
||||
StNodeType.MEMVAR -> print("(M) ")
|
||||
StNodeType.MEMORYSLAB -> print("(MS) ")
|
||||
StNodeType.CONSTANT -> print("(C) ")
|
||||
StNodeType.BUILTINFUNC -> print("(F) ")
|
||||
StNodeType.ROMSUB -> print("(R) ")
|
||||
}
|
||||
printProperties()
|
||||
println()
|
||||
children.forEach { (_, node) -> node.printIndented(indent+1) }
|
||||
}
|
||||
|
||||
open fun printProperties() {
|
||||
print("$name ")
|
||||
}
|
||||
|
||||
fun add(child: StNode) {
|
||||
children[child.name] = child
|
||||
child.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
class StStaticVariable(name: String,
|
||||
val dt: DataType,
|
||||
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
|
||||
val onetimeInitializationStringValue: StString?,
|
||||
val onetimeInitializationArrayValue: StArray?,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
val zpwish: ZeropageWish,
|
||||
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
|
||||
|
||||
init {
|
||||
if(length!=null) {
|
||||
require(onetimeInitializationNumericValue == null)
|
||||
if(onetimeInitializationArrayValue!=null)
|
||||
require(length == onetimeInitializationArrayValue.size)
|
||||
}
|
||||
if(onetimeInitializationNumericValue!=null)
|
||||
require(dt in NumericDatatypes)
|
||||
if(onetimeInitializationArrayValue!=null)
|
||||
require(dt in ArrayDatatypes)
|
||||
if(onetimeInitializationStringValue!=null) {
|
||||
require(dt == DataType.STR)
|
||||
require(length == onetimeInitializationStringValue.first.length+1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun printProperties() {
|
||||
print("$name dt=$dt zpw=$zpwish")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
|
||||
StNode(name, StNodeType.CONSTANT, position) {
|
||||
override fun printProperties() {
|
||||
print("$name dt=$dt value=$value")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
position: Position) :
|
||||
StNode(name, StNodeType.MEMVAR, position) {
|
||||
override fun printProperties() {
|
||||
print("$name dt=$dt address=${address.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
val align: UInt,
|
||||
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator. TODO remove this once no longer used
|
||||
position: Position
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, position) {
|
||||
override fun printProperties() {
|
||||
print("$name size=$size align=$align address=$allocatedAddress")
|
||||
}
|
||||
}
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
|
||||
StNode(name, StNodeType.SUBROUTINE, position) {
|
||||
override fun printProperties() {
|
||||
print(name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StRomSub(name: String,
|
||||
val address: UInt,
|
||||
val parameters: List<StRomSubParameter>,
|
||||
val returns: List<RegisterOrStatusflag>,
|
||||
position: Position) :
|
||||
StNode(name, StNodeType.ROMSUB, position) {
|
||||
override fun printProperties() {
|
||||
print("$name address=${address.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StSubroutineParameter(val name: String, val type: DataType)
|
||||
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOf: List<String>?)
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
@ -1,144 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
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 printIndented(indent: Int) {
|
||||
print(" ".repeat(indent))
|
||||
print("${this.javaClass.simpleName} ")
|
||||
printProperties()
|
||||
println()
|
||||
children.forEach { it.printIndented(indent+1) }
|
||||
}
|
||||
|
||||
abstract fun printProperties()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY) {
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
|
||||
val scopedName: List<String> by lazy {
|
||||
var namedParent: PtNode = this.parent
|
||||
if(namedParent is PtProgram)
|
||||
listOf(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 print() = printIndented(0)
|
||||
override fun printProperties() {
|
||||
print("'$name'")
|
||||
}
|
||||
|
||||
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||
|
||||
fun allBlocks(): Sequence<PtBlock> =
|
||||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
class PtBlock(name: String,
|
||||
val address: UInt?,
|
||||
val library: Boolean,
|
||||
val forceOutput: Boolean,
|
||||
val alignment: BlockAlignment,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment")
|
||||
}
|
||||
|
||||
enum class BlockAlignment {
|
||||
NONE,
|
||||
WORD,
|
||||
PAGE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) {
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print(name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtBreakpoint(position: Position): PtNode(position) {
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) {
|
||||
override fun printProperties() {
|
||||
print("filename=$file offset=$offset length=$length")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtNop(position: Position): PtNode(position) {
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtScopeVarsDecls(position: Position): PtNode(position) {
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.Position
|
||||
import java.util.*
|
||||
import kotlin.math.round
|
||||
|
||||
|
||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type==DataType.UNDEFINED) {
|
||||
@Suppress("LeakingThis")
|
||||
when(this) {
|
||||
is PtBuiltinFunctionCall -> { /* void function call */ }
|
||||
is PtFunctionCall -> { /* void function call */ }
|
||||
is PtIdentifier -> { /* non-variable identifier */ }
|
||||
else -> throw IllegalArgumentException("type should be known @$position")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun printProperties() {
|
||||
print(type)
|
||||
}
|
||||
|
||||
infix fun isSameAs(other: PtExpression): Boolean {
|
||||
return when(this) {
|
||||
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
||||
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.targetName==targetName
|
||||
is PtMachineRegister -> other is PtMachineRegister && 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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||
val identifier: PtIdentifier
|
||||
get() = children.single() as PtIdentifier
|
||||
}
|
||||
|
||||
|
||||
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
}
|
||||
|
||||
|
||||
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtBuiltinFunctionCall(val name: String,
|
||||
val void: Boolean,
|
||||
val hasNoSideEffects: Boolean,
|
||||
type: DataType,
|
||||
position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val args: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
override fun printProperties() {
|
||||
print("$name void=$void noSideFx=$hasNoSideEffects")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
|
||||
val left: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val right: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
override fun printProperties() {
|
||||
print("$operator -> $type")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||
val element: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val iterable: PtIdentifier
|
||||
get() = children[1] as PtIdentifier
|
||||
}
|
||||
|
||||
|
||||
class PtFunctionCall(val functionName: List<String>,
|
||||
val void: Boolean,
|
||||
type: DataType,
|
||||
position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val args: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
override fun printProperties() {
|
||||
print("${functionName.joinToString(".")} void=$void")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtIdentifier(val ref: List<String>, val targetName: List<String>, type: DataType, position: Position) : PtExpression(type, position) {
|
||||
override fun printProperties() {
|
||||
print("$ref --> $targetName $type")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||
val address: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type!=DataType.FLOAT) {
|
||||
val rounded = round(number)
|
||||
if (rounded != number)
|
||||
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||
}
|
||||
}
|
||||
|
||||
override fun printProperties() {
|
||||
print("$number ($type)")
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(type, number)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtNumber)
|
||||
return false
|
||||
return number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||
}
|
||||
|
||||
|
||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
val value: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
|
||||
init {
|
||||
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||
if(operator !in setOf("+", "-", "~"))
|
||||
throw IllegalArgumentException("invalid prefix operator: $operator")
|
||||
}
|
||||
|
||||
override fun printProperties() {
|
||||
print(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
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
||||
override fun printProperties() {
|
||||
print("$encoding:\"$value\"")
|
||||
}
|
||||
|
||||
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: DataType, position: Position) : PtExpression(type, position) {
|
||||
val value: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
}
|
||||
|
||||
|
||||
// special node that isn't created from compiling user code, but used internally
|
||||
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) {
|
||||
override fun printProperties() {
|
||||
print("reg=$register $type")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
|
@ -1,220 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
class PtAsmSub(
|
||||
name: String,
|
||||
val address: UInt?,
|
||||
val clobbers: Set<CpuRegister>,
|
||||
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
|
||||
val returnTypes: List<DataType>, // TODO join with register as Pairs ?
|
||||
val retvalRegisters: List<RegisterOrStatusflag>,
|
||||
val inline: Boolean,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print("$name inline=$inline")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtSub(
|
||||
name: String,
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
val inline: Boolean,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print(name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtSubroutineParameter(val name: String, val type: DataType, position: Position): PtNode(position) {
|
||||
override fun printProperties() {
|
||||
print("$type $name")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtAssignment(position: Position) : PtNode(position) {
|
||||
val target: PtAssignTarget
|
||||
get() = children[0] as PtAssignTarget
|
||||
val value: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
override fun printProperties() { }
|
||||
|
||||
val isInplaceAssign: Boolean by lazy {
|
||||
val target = target.children.single() as PtExpression
|
||||
when(val source = value) {
|
||||
is PtArrayIndexer -> {
|
||||
if(target is PtArrayIndexer && source.type==target.type) {
|
||||
if(target.variable isSameAs source.variable) {
|
||||
target.index isSameAs source.index
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.targetName==source.targetName
|
||||
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
|
||||
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
|
||||
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
|
||||
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
|
||||
is PtPrefix -> {
|
||||
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
|
||||
||
|
||||
(target is PtIdentifier && (source.value as? PtIdentifier)?.targetName==target.targetName)
|
||||
}
|
||||
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
|
||||
is PtBinaryExpression ->
|
||||
target isSameAs source.left
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PtAssignTarget(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")
|
||||
}
|
||||
}
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
override fun printProperties() {
|
||||
print(condition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtIfElse(position: Position) : PtNode(position) {
|
||||
val condition: PtBinaryExpression
|
||||
get() = children[0] as PtBinaryExpression
|
||||
val ifScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtJump(val identifier: PtIdentifier?,
|
||||
val address: UInt?,
|
||||
val generatedLabel: String?,
|
||||
position: Position) : PtNode(position) {
|
||||
override fun printProperties() {
|
||||
identifier?.printProperties()
|
||||
if(address!=null) print(address.toHex())
|
||||
if(generatedLabel!=null) print(generatedLabel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
||||
val target: PtAssignTarget
|
||||
get() = children.single() as PtAssignTarget
|
||||
|
||||
override fun printProperties() {
|
||||
print(operator)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||
val count: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtReturn(position: Position) : PtNode(position) {
|
||||
val hasValue = children.any()
|
||||
val value: PtExpression?
|
||||
get() {
|
||||
return if(children.any())
|
||||
children.single() as PtExpression
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print("$type $name")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print("$type $name = $value")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtMemMapped(name: String, val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position) {
|
||||
override fun printProperties() {
|
||||
print("&$type $name = ${address.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
val value: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val choices: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
override fun printProperties() {}
|
||||
}
|
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"
|
||||
)
|
@ -8,20 +8,38 @@ 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 romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var slowCodegenWarnings: Boolean = false,
|
||||
var memtopAddress: UInt,
|
||||
var warnSymbolShadowing: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var optimizeFloatExpressions: Boolean = false,
|
||||
var dontReinitGlobals: 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 keepIR: Boolean = false,
|
||||
var evalStackBaseAddress: UInt? = null,
|
||||
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 quiet: Boolean = false,
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
)
|
||||
) {
|
||||
init {
|
||||
compTarget.initializeMemoryAreas(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
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)
|
||||
@ -14,7 +20,7 @@ fun Number.toHex(): String {
|
||||
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 -> throw IllegalArgumentException("number too large for 16 bits $this")
|
||||
else -> "$"+integer.toString(16).padStart(8,'0')
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,11 +28,12 @@ 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 -> throw IllegalArgumentException("number too large for 16 bits $this")
|
||||
else -> "$"+this.toString(16).padStart(8,'0')
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +65,7 @@ fun String.unescape(): String {
|
||||
'\\' -> '\\'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
't' -> '\t'
|
||||
'"' -> '"'
|
||||
'\'' -> '\''
|
||||
'u' -> {
|
||||
|
@ -1,58 +1,241 @@
|
||||
package prog8.code.core
|
||||
|
||||
enum class DataType {
|
||||
UBYTE, // pass by value
|
||||
BYTE, // pass by value
|
||||
UWORD, // pass by value
|
||||
WORD, // pass by value
|
||||
FLOAT, // pass by value
|
||||
BOOL, // pass by value
|
||||
import java.util.*
|
||||
|
||||
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_UB, // pass by reference
|
||||
ARRAY_B, // pass by reference
|
||||
ARRAY_UW, // pass by reference
|
||||
ARRAY_W, // pass by reference
|
||||
ARRAY_F, // pass by reference
|
||||
ARRAY_BOOL, // 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;
|
||||
|
||||
/**
|
||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(this) {
|
||||
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, FLOAT)
|
||||
FLOAT -> targetType.oneOf(FLOAT)
|
||||
STR -> targetType.oneOf(STR, UWORD)
|
||||
in ArrayDatatypes -> targetType == this
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun oneOf(vararg types: DataType) = this in types
|
||||
|
||||
infix fun largerThan(other: DataType) =
|
||||
fun largerSizeThan(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this in ByteDatatypes -> false
|
||||
this in WordDatatypes -> other in ByteDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> 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
|
||||
}
|
||||
|
||||
infix fun equalsSize(other: DataType) =
|
||||
fun equalsSize(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this in ByteDatatypes -> other in ByteDatatypes
|
||||
this in WordDatatypes -> other in WordDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> 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 {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.BOOL -> "bool[]"
|
||||
BaseDataType.FLOAT -> "float[]"
|
||||
BaseDataType.BYTE -> "byte[]"
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.WORD -> "word[] (split)"
|
||||
BaseDataType.UWORD -> "uword[] (split)"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
fun sourceString(): String = when (base) {
|
||||
BaseDataType.BOOL -> "bool"
|
||||
BaseDataType.UBYTE -> "ubyte"
|
||||
BaseDataType.BYTE -> "byte"
|
||||
BaseDataType.UWORD -> "uword"
|
||||
BaseDataType.WORD -> "word"
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
BaseDataType.UWORD -> "@nosplit uword["
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.UWORD -> "uword["
|
||||
BaseDataType.WORD -> "word["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
|
||||
}
|
||||
|
||||
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(base) {
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
val isSignedByte = base == BaseDataType.BYTE
|
||||
val isByteOrBool = base.isByteOrBool
|
||||
val isWord = base.isWord
|
||||
val isUnsignedWord = base == BaseDataType.UWORD
|
||||
val isSignedWord = base == BaseDataType.WORD
|
||||
val isInteger = base.isInteger
|
||||
val isIntegerOrBool = base.isIntegerOrBool
|
||||
val isNumeric = base.isNumeric
|
||||
val isNumericOrBool = base.isNumericOrBool
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
}
|
||||
|
||||
|
||||
enum class CpuRegister {
|
||||
A,
|
||||
X,
|
||||
@ -73,7 +256,14 @@ enum class RegisterOrPair {
|
||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
|
||||
return when(cpu) {
|
||||
CpuRegister.A -> A
|
||||
CpuRegister.X -> X
|
||||
CpuRegister.Y -> Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun asCpuRegister(): CpuRegister = when(this) {
|
||||
@ -83,6 +273,17 @@ enum class RegisterOrPair {
|
||||
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 {
|
||||
@ -92,7 +293,7 @@ enum class Statusflag {
|
||||
Pn; // don't use
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,44 +309,9 @@ enum class BranchCondition {
|
||||
PL, // PL == POS
|
||||
POS,
|
||||
VS,
|
||||
VC,
|
||||
VC
|
||||
}
|
||||
|
||||
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
val IterableDatatypes = arrayOf(
|
||||
DataType.STR,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||
)
|
||||
val PassByValueDatatypes = NumericDatatypes
|
||||
val PassByReferenceDatatypes = IterableDatatypes
|
||||
val ArrayToElementTypes = mapOf(
|
||||
DataType.STR to DataType.UBYTE,
|
||||
DataType.ARRAY_B to DataType.BYTE,
|
||||
DataType.ARRAY_UB to DataType.UBYTE,
|
||||
DataType.ARRAY_W to DataType.WORD,
|
||||
DataType.ARRAY_UW to DataType.UWORD,
|
||||
DataType.ARRAY_F to DataType.FLOAT,
|
||||
DataType.ARRAY_BOOL to DataType.BOOL
|
||||
)
|
||||
val ElementToArrayTypes = mapOf(
|
||||
DataType.BYTE to DataType.ARRAY_B,
|
||||
DataType.UBYTE to DataType.ARRAY_UB,
|
||||
DataType.WORD to DataType.ARRAY_W,
|
||||
DataType.UWORD to DataType.ARRAY_UW,
|
||||
DataType.FLOAT to DataType.ARRAY_F,
|
||||
DataType.BOOL to DataType.ARRAY_BOOL
|
||||
)
|
||||
val Cx16VirtualRegisters = arrayOf(
|
||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
||||
@ -153,12 +319,17 @@ val Cx16VirtualRegisters = arrayOf(
|
||||
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
|
||||
XEX,
|
||||
LIBRARY
|
||||
}
|
||||
|
||||
enum class CbmPrgLauncherType {
|
||||
@ -180,3 +351,9 @@ enum class ZeropageWish {
|
||||
DONTCARE,
|
||||
NOT_IN_ZEROPAGE
|
||||
}
|
||||
|
||||
enum class SplitWish {
|
||||
DONTCARE,
|
||||
SPLIT,
|
||||
NOSPLIT
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package prog8.code.core
|
||||
|
||||
interface IAssemblyGenerator {
|
||||
fun compileToAssembly(): IAssemblyProgram?
|
||||
}
|
||||
|
||||
interface IAssemblyProgram {
|
||||
val name: String
|
||||
fun assemble(options: CompilationOptions): Boolean
|
||||
}
|
||||
|
||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
@ -1,11 +1,44 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65C02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
val name: String
|
||||
val machine: IMachineDefinition
|
||||
val supportedEncodings: Set<Encoding>
|
||||
val defaultEncoding: Encoding
|
||||
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
}
|
||||
|
@ -3,10 +3,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) {
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||
if(numErrors>0)
|
||||
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
|
||||
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
var ESTACK_LO: UInt
|
||||
var ESTACK_HI: UInt
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
|
||||
val opcodeNames: Set<String>
|
||||
var zeropage: Zeropage
|
||||
val cpu: CpuType
|
||||
|
||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
fun overrideEvalStack(evalStackBaseAddress: UInt) {
|
||||
require(evalStackBaseAddress and 255u == 0u)
|
||||
ESTACK_LO = evalStackBaseAddress
|
||||
ESTACK_HI = evalStackBaseAddress + 256u
|
||||
}
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
package prog8.code.core
|
||||
|
||||
interface IMemSizer {
|
||||
fun memorySize(dt: DataType): Int
|
||||
fun memorySize(arrayDt: DataType, numElements: Int): Int
|
||||
fun memorySize(dt: DataType, numElements: Int?): Int
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.UWORD, null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
throw IllegalArgumentException(x.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,17 @@ enum class Encoding(val prefix: String) {
|
||||
PETSCII("petscii"), // c64/c128/cx16
|
||||
SCREENCODES("sc"), // c64/c128/cx16
|
||||
ATASCII("atascii"), // atari
|
||||
ISO("iso") // cx16
|
||||
ISO("iso"), // cx16 (iso-8859-15)
|
||||
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
||||
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: List<UByte>, encoding: Encoding): String
|
||||
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.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.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"))
|
||||
}
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||
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) {
|
||||
|
@ -1,16 +1,21 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.sanitize
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||
fun toClickableStr(): String {
|
||||
if(this===DUMMY)
|
||||
return ""
|
||||
if(SourceCode.isLibraryResource(file))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
val path = Path(file).sanitize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(x: InvalidPathException) {
|
||||
} catch(_: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
"file://$file:$line:$startCol:"
|
||||
}
|
||||
|
@ -1,122 +0,0 @@
|
||||
package prog8.code.core
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
|
||||
|
||||
class ZeropageAllocationError(message: String) : Exception(message)
|
||||
|
||||
|
||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||
|
||||
data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int)
|
||||
|
||||
// the variables allocated into Zeropage.
|
||||
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||
val allocatedVariables = mutableMapOf<List<String>, ZpAllocation>()
|
||||
|
||||
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(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
fun allocate(name: List<String>,
|
||||
datatype: DataType,
|
||||
numElements: Int?,
|
||||
position: Position?,
|
||||
errors: IErrorReporter
|
||||
): Result<Pair<UInt, Int>, ZeropageAllocationError> {
|
||||
|
||||
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
||||
|
||||
if(options.zeropage== ZeropageType.DONTUSE)
|
||||
return Err(ZeropageAllocationError("zero page usage has been disabled"))
|
||||
|
||||
val size: Int =
|
||||
when (datatype) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
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.FLOAT -> {
|
||||
if (options.floats) {
|
||||
val memsize = options.compTarget.memorySize(DataType.FLOAT)
|
||||
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(ZeropageAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> return Err(ZeropageAllocationError("cannot put datatype $datatype in zeropage"))
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if(free.size > 0) {
|
||||
if(size==1) {
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||
if(oneSeparateByteFree(candidate))
|
||||
return Ok(Pair(makeAllocation(candidate, 1, datatype, name), 1))
|
||||
}
|
||||
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 1))
|
||||
}
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||
if (sequentialFree(candidate, size))
|
||||
return Ok(Pair(makeAllocation(candidate, size, datatype, name), size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Err(ZeropageAllocationError("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: List<String>): UInt {
|
||||
require(size>=0)
|
||||
free.removeAll(address until address+size.toUInt())
|
||||
if(name.isNotEmpty()) {
|
||||
allocatedVariables[name] = when(datatype) {
|
||||
in NumericDatatypes -> ZpAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
DataType.STR -> ZpAllocation(address, datatype, size)
|
||||
in ArrayDatatypes -> ZpAllocation(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())
|
||||
}
|
||||
|
||||
abstract fun allocateCx16VirtualRegisters()
|
||||
}
|
74
codeCore/src/prog8/code/source/ImportFileSystem.kt
Normal file
74
codeCore/src/prog8/code/source/ImportFileSystem.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.sanitize
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
|
||||
|
||||
// Resource caching "filesystem".
|
||||
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
|
||||
|
||||
object ImportFileSystem {
|
||||
|
||||
fun expandTilde(path: String): String = if (path.startsWith("~")) {
|
||||
val userHome = System.getProperty("user.home")
|
||||
userHome + path.drop(1)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||
val normalized = path.sanitize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
val file = SourceCode.File(normalized, isLibrary)
|
||||
cache[normalized.toString()] = file
|
||||
return file
|
||||
}
|
||||
|
||||
fun getResource(name: String): SourceCode {
|
||||
val cached = cache[name]
|
||||
if (cached != null) return cached
|
||||
val resource = SourceCode.Resource(name)
|
||||
cache[name] = resource
|
||||
return resource
|
||||
}
|
||||
|
||||
fun retrieveSourceLine(position: Position): String {
|
||||
if(SourceCode.isLibraryResource(position.file)) {
|
||||
val cached = cache[SourceCode.withoutPrefix(position.file)]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
}
|
||||
val cached = cache[position.file]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).sanitize()
|
||||
val cached2 = cache[path.toString()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
throw NoSuchElementException("cannot get source line $position, with path $path")
|
||||
}
|
||||
|
||||
private fun getLine(code: SourceCode, lineIndex: Int): String {
|
||||
var spans = lineSpanCache[code]
|
||||
if(spans==null) {
|
||||
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
|
||||
val ends = lineSpans.drop(1) + code.text.length
|
||||
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
|
||||
lineSpanCache[code] = spans
|
||||
}
|
||||
val span = spans[lineIndex - 1]
|
||||
return code.text.substring(span.start, span.end).trim()
|
||||
}
|
||||
|
||||
private class LineSpan(val start: Int, val end: Int)
|
||||
|
||||
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
|
||||
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
|
||||
}
|
@ -1,15 +1,13 @@
|
||||
package prog8.code.core
|
||||
package prog8.code.source
|
||||
|
||||
import java.io.File
|
||||
import prog8.code.sanitize
|
||||
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
|
||||
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
||||
*/
|
||||
@ -25,6 +23,11 @@ sealed class SourceCode {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -54,34 +57,46 @@ sealed class SourceCode {
|
||||
/**
|
||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||
*/
|
||||
const val libraryFilePrefix = "library:"
|
||||
const val stringSourcePrefix = "string:"
|
||||
val curdir: Path = Path(".").toAbsolutePath()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||
fun isRegularFilesystemPath(pathString: String) =
|
||||
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
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(override val text: String): SourceCode() {
|
||||
class Text(origText: String): SourceCode() {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
|
||||
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
|
||||
*/
|
||||
class File(path: Path): SourceCode() {
|
||||
internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
|
||||
override val text: String
|
||||
override val origin: String
|
||||
override val name: String
|
||||
@ -92,7 +107,8 @@ sealed class SourceCode {
|
||||
val normalized = path.normalize()
|
||||
origin = relative(normalized).toString()
|
||||
try {
|
||||
text = normalized.readText()
|
||||
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = normalized.toFile().nameWithoutExtension
|
||||
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||
@ -104,13 +120,15 @@ sealed class SourceCode {
|
||||
|
||||
/**
|
||||
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
||||
* You can only get an instance of this via the ImportFileSystem object.
|
||||
*/
|
||||
class Resource(pathString: String): SourceCode() {
|
||||
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
|
||||
internal class Resource(pathString: String): SourceCode() {
|
||||
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
|
||||
|
||||
override val isFromResources = true
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$libraryFilePrefix$normalized"
|
||||
override val isFromLibrary = true
|
||||
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||
override val text: String
|
||||
override val name: String
|
||||
|
||||
@ -119,13 +137,14 @@ sealed class SourceCode {
|
||||
if (rscURL == null) {
|
||||
val rscRoot = object {}.javaClass.getResource("/")
|
||||
throw NoSuchFileException(
|
||||
File(normalized),
|
||||
java.io.File(normalized),
|
||||
reason = "looked in resources rooted at $rscRoot"
|
||||
)
|
||||
}
|
||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||
text = stream!!.reader().use { it.readText() }
|
||||
name = Path.of(pathString).toFile().nameWithoutExtension
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,7 +154,8 @@ sealed class SourceCode {
|
||||
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>"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.atari.AtariMachineDefinition
|
||||
|
||||
|
||||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = AtariMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.ATASCII)
|
||||
override val defaultEncoding = Encoding.ATASCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "atari"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
@ -1,20 +1,81 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.c128.C128MachineDefinition
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
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 CbmMemorySizer {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = C128MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
}
|
||||
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,20 +1,111 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.c64.C64MachineDefinition
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
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 CbmMemorySizer {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = C64MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
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, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
if(!quiet)
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
|
||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(_: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
|
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
173
codeCore/src/prog8/code/target/ConfigFileTarget.kt
Normal file
@ -0,0 +1,173 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.ImportFileSystem.expandTilde
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.ConfigurableZeropage
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
|
||||
|
||||
class ConfigFileTarget(
|
||||
override val name: String,
|
||||
override val defaultEncoding: Encoding,
|
||||
override val cpu: CpuType,
|
||||
override val PROGRAM_LOAD_ADDRESS: UInt,
|
||||
override val PROGRAM_MEMTOP_ADDRESS: UInt,
|
||||
override val STARTUP_CODE_RESERVED_SIZE: UInt,
|
||||
override val BSSHIGHRAM_START: UInt,
|
||||
override val BSSHIGHRAM_END: UInt,
|
||||
override val BSSGOLDENRAM_START: UInt,
|
||||
override val BSSGOLDENRAM_END: UInt,
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
val zpScratchW1: UInt,
|
||||
val zpScratchW2: UInt,
|
||||
val virtualregistersStart: UInt,
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
private fun Properties.getString(property: String): String {
|
||||
val value = this.getProperty(property, null)
|
||||
if(value!=null)
|
||||
return value
|
||||
throw NoSuchElementException("string property '$property' not found in config file")
|
||||
}
|
||||
|
||||
private fun Properties.getInteger(property: String): UInt {
|
||||
val value = this.getProperty(property, null)
|
||||
if(value!=null) return parseInt(value)
|
||||
throw NoSuchElementException("integer property '$property' not found in config file")
|
||||
}
|
||||
|
||||
private fun parseInt(value: String): UInt {
|
||||
if(value.startsWith("0x"))
|
||||
return value.drop(2).toUInt(16)
|
||||
if(value.startsWith("$"))
|
||||
return value.drop(1).toUInt(16)
|
||||
if(value.startsWith("%"))
|
||||
return value.drop(1).toUInt(2)
|
||||
return value.toUInt()
|
||||
}
|
||||
|
||||
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
|
||||
val rangesStr = props.getString(key)
|
||||
if(rangesStr.isBlank())
|
||||
return emptyList()
|
||||
val result = mutableListOf<UIntRange>()
|
||||
val ranges = rangesStr.split(",").map { it.trim() }
|
||||
for(r in ranges) {
|
||||
if ('-' in r) {
|
||||
val (fromStr, toStr) = r.split("-")
|
||||
val from = parseInt(fromStr.trim())
|
||||
val to = parseInt(toStr.trim())
|
||||
result.add(from..to)
|
||||
} else {
|
||||
val address = parseInt(r)
|
||||
result.add(address..address)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun fromConfigFile(configfile: Path): ConfigFileTarget {
|
||||
val props = Properties()
|
||||
props.load(configfile.inputStream())
|
||||
|
||||
val cpuString = props.getString("cpu").uppercase()
|
||||
val cpuType = try {
|
||||
CpuType.valueOf(cpuString)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
CpuType.valueOf("CPU$cpuString")
|
||||
}
|
||||
val ioAddresses = parseAddressRanges("io_regions", props)
|
||||
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
|
||||
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
|
||||
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
|
||||
|
||||
val libraryPath = expandTilde(Path(props.getString("library")))
|
||||
if(!libraryPath.isDirectory())
|
||||
throw IOException("invalid library path: $libraryPath")
|
||||
|
||||
val customLauncherStr = props.getProperty("custom_launcher_code", null)
|
||||
val customLauncher =
|
||||
if(customLauncherStr?.isNotBlank()==true)
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
return ConfigFileTarget(
|
||||
configfile.nameWithoutExtension,
|
||||
Encoding.entries.first { it.prefix==props.getString("encoding") },
|
||||
cpuType,
|
||||
props.getInteger("load_address"),
|
||||
props.getInteger("memtop"),
|
||||
0u, // used only in a very specific error condition check in a certain scenario...
|
||||
props.getInteger("bss_highram_start"),
|
||||
props.getInteger("bss_highram_end"),
|
||||
props.getInteger("bss_goldenram_start"),
|
||||
props.getInteger("bss_goldenram_end"),
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
props.getInteger("zp_scratch_w1"),
|
||||
props.getInteger("zp_scratch_w2"),
|
||||
props.getInteger("virtual_registers"),
|
||||
zpFullsafe,
|
||||
zpKernalsafe,
|
||||
zpBasicsafe,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO floats are not yet supported here, just enter some values
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 8
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam // TODO this is not yet used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = ConfigurableZeropage(
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||
virtualregistersStart,
|
||||
zpBasicsafe,
|
||||
zpKernalsafe,
|
||||
zpFullsafe,
|
||||
compilerOptions
|
||||
)
|
||||
// note: there's no golden ram yet
|
||||
}
|
||||
}
|
@ -1,20 +1,94 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.target.cx16.CX16MachineDefinition
|
||||
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 CbmMemorySizer {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = CX16MachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
}
|
||||
|
||||
|
||||
override val cpu = CpuType.CPU65C02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
override val BSSGOLDENRAM_START = 0x0400u
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package prog8.code.target.cbm
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import 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 {
|
34
codeCore/src/prog8/code/target/NormalMemSizer.kt
Normal file
34
codeCore/src/prog8/code/target/NormalMemSizer.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * floatsize
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
80
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
80
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
@ -0,0 +1,80 @@
|
||||
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(true),
|
||||
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
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
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, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting PET emulator...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +1,111 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = VirtualMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.ISO)
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
const val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number): String {
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "$$it" }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val bits = num.toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||
return parts
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==8) { "need 8 bytes" }
|
||||
val b0 = bytes[0].toLong() shl (8*7)
|
||||
val b1 = bytes[1].toLong() shl (8*6)
|
||||
val b2 = bytes[2].toLong() shl (8*5)
|
||||
val b3 = bytes[3].toLong() shl (8*4)
|
||||
val b4 = bytes[4].toLong() shl (8*3)
|
||||
val b5 = bytes[5].toLong() shl (8*2)
|
||||
val b6 = bytes[6].toLong() shl (8*1)
|
||||
val b7 = bytes[7].toLong() shl (8*0)
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(!quiet)
|
||||
println("\nStarting Virtual Machine...")
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText(), quiet)
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText(), quiet)
|
||||
else
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String, quiet: Boolean)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
override val SCRATCH_B1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_REG: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.c64.normal6502instructions
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class AtariMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
|
||||
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.output == OutputType.XEX)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
val emulatorName: String
|
||||
val cmdline: List<String>
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulatorName = "atari800"
|
||||
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
|
||||
}
|
||||
2 -> {
|
||||
emulatorName = "altirra"
|
||||
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Atari target only supports atari800 and altirra emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO monlist?
|
||||
|
||||
println("\nStarting Atari800XL emulator $emulatorName...")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = AtariZeropage(compilerOptions)
|
||||
}
|
||||
|
||||
override val opcodeNames = normal6502instructions
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
||||
free.addAll(0x00u..0xffu)
|
||||
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
}
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if atari can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package prog8.code.target.c128
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.c64.normal6502instructions
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
|
||||
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
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 = 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 initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
override val opcodeNames = normal6502instructions
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package prog8.code.target.c128
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
|
||||
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x9cu // temp storage for a register, 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'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
|
||||
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
|
||||
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE,
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.clear() // TODO c128 usable zero page addresses
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if C128 can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package prog8.code.target.c64
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
|
||||
override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
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(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
override val opcodeNames = normal6502instructions
|
||||
}
|
||||
|
||||
|
||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||
internal val normal6502instructions = setOf(
|
||||
"adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
|
||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
|
||||
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
|
||||
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
|
||||
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
|
||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
@ -1,18 +0,0 @@
|
||||
package prog8.code.target.cbm
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal object CbmMemorySizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package prog8.code.target.cx16
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
|
||||
override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = emptyList()
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
extraArgs = listOf("-sym", 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", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
// 65c02 opcodes, these cannot be used as variable or label names
|
||||
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
|
||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
|
||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||
"inc", "inx", "iny", "jmp", "jsr",
|
||||
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
|
||||
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
|
||||
"sec", "sed", "sei",
|
||||
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
|
||||
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
|
||||
"rmb", "smb", "stp", "wai")
|
||||
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
@ -26,7 +26,7 @@ object AtasciiEncoding {
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
@ -62,7 +62,7 @@ object AtasciiEncoding {
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
@ -80,7 +80,7 @@ object AtasciiEncoding {
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
@ -208,7 +209,7 @@ object AtasciiEncoding {
|
||||
return Ok(mapped)
|
||||
}
|
||||
|
||||
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
||||
}
|
||||
}
|
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
327
codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
Normal file
@ -0,0 +1,327 @@
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
|
||||
object C64osEncoding {
|
||||
|
||||
// decoding: from C64 OS Screencodes (0-255) to unicode
|
||||
// character table from:
|
||||
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
|
||||
|
||||
private val decodingC64os = charArrayOf(
|
||||
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
||||
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
||||
'c' , // c 0x03 -> LATIN SMALL LETTER C
|
||||
'd' , // d 0x04 -> LATIN SMALL LETTER D
|
||||
'e' , // e 0x05 -> LATIN SMALL LETTER E
|
||||
'f' , // f 0x06 -> LATIN SMALL LETTER F
|
||||
'g' , // g 0x07 -> LATIN SMALL LETTER G
|
||||
'h' , // h 0x08 -> LATIN SMALL LETTER H
|
||||
'i' , // i 0x09 -> LATIN SMALL LETTER I
|
||||
'j' , // j 0x0A -> LATIN SMALL LETTER J
|
||||
'k' , // k 0x0B -> LATIN SMALL LETTER K
|
||||
'l' , // l 0x0C -> LATIN SMALL LETTER L
|
||||
'm' , // m 0x0D -> LATIN SMALL LETTER M
|
||||
'n' , // n 0x0E -> LATIN SMALL LETTER N
|
||||
'o' , // o 0x0F -> LATIN SMALL LETTER O
|
||||
'p' , // p 0x10 -> LATIN SMALL LETTER P
|
||||
'q' , // q 0x11 -> LATIN SMALL LETTER Q
|
||||
'r' , // r 0x12 -> LATIN SMALL LETTER R
|
||||
's' , // s 0x13 -> LATIN SMALL LETTER S
|
||||
't' , // t 0x14 -> LATIN SMALL LETTER T
|
||||
'u' , // u 0x15 -> LATIN SMALL LETTER U
|
||||
'v' , // v 0x16 -> LATIN SMALL LETTER V
|
||||
'w' , // w 0x17 -> LATIN SMALL LETTER W
|
||||
'x' , // x 0x18 -> LATIN SMALL LETTER X
|
||||
'y' , // y 0x19 -> LATIN SMALL LETTER Y
|
||||
'z' , // z 0x1A -> LATIN SMALL LETTER Z
|
||||
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
|
||||
'\\' , // \ 0x1C -> REVERSE SOLIDUS
|
||||
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
|
||||
'^' , // ^ 0x1E -> CIRCUMFLEX
|
||||
'_' , // _ 0x1F -> UNDERSCORE
|
||||
' ' , // 0x20 -> SPACE
|
||||
'!' , // ! 0x21 -> EXCLAMATION MARK
|
||||
'"' , // " 0x22 -> QUOTATION MARK
|
||||
'#' , // # 0x23 -> NUMBER SIGN
|
||||
'$' , // $ 0x24 -> DOLLAR SIGN
|
||||
'%' , // % 0x25 -> PERCENT SIGN
|
||||
'&' , // & 0x26 -> AMPERSAND
|
||||
'\'' , // ' 0x27 -> APOSTROPHE
|
||||
'(' , // ( 0x28 -> LEFT PARENTHESIS
|
||||
')' , // ) 0x29 -> RIGHT PARENTHESIS
|
||||
'*' , // * 0x2A -> ASTERISK
|
||||
'+' , // + 0x2B -> PLUS SIGN
|
||||
',' , // , 0x2C -> COMMA
|
||||
'-' , // - 0x2D -> HYPHEN-MINUS
|
||||
'.' , // . 0x2E -> FULL STOP
|
||||
'/' , // / 0x2F -> SOLIDUS
|
||||
'0' , // 0 0x30 -> DIGIT ZERO
|
||||
'1' , // 1 0x31 -> DIGIT ONE
|
||||
'2' , // 2 0x32 -> DIGIT TWO
|
||||
'3' , // 3 0x33 -> DIGIT THREE
|
||||
'4' , // 4 0x34 -> DIGIT FOUR
|
||||
'5' , // 5 0x35 -> DIGIT FIVE
|
||||
'6' , // 6 0x36 -> DIGIT SIX
|
||||
'7' , // 7 0x37 -> DIGIT SEVEN
|
||||
'8' , // 8 0x38 -> DIGIT EIGHT
|
||||
'9' , // 9 0x39 -> DIGIT NINE
|
||||
':' , // : 0x3A -> COLON
|
||||
';' , // ; 0x3B -> SEMICOLON
|
||||
'<' , // < 0x3C -> LESS-THAN SIGN
|
||||
'=' , // = 0x3D -> EQUALS SIGN
|
||||
'>' , // > 0x3E -> GREATER-THAN SIGN
|
||||
'?' , // ? 0x3F -> QUESTION MARK
|
||||
'`' , // ` 0x40 -> GRAVE ACCENT
|
||||
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
|
||||
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
|
||||
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
|
||||
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
|
||||
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
|
||||
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
|
||||
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
|
||||
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
|
||||
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
|
||||
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
|
||||
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
|
||||
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
|
||||
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
|
||||
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
|
||||
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
|
||||
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
|
||||
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
|
||||
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
|
||||
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
|
||||
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
|
||||
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
|
||||
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
|
||||
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
|
||||
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
|
||||
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
|
||||
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
|
||||
'{' , // { 0x5B -> LEFT BRACE
|
||||
'|' , // | 0x5C -> VERTICAL BAR
|
||||
'}' , // } 0x5D -> RIGHT BRACE
|
||||
'~' , // ~ 0x5E -> TILDE
|
||||
'\ufffe', // 0x5F -> RESERVED
|
||||
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
|
||||
'\ufffe', // 0x61 -> COMMODORE SYMBOL
|
||||
'\u2191', // ↑ 0x62 -> UP ARROW
|
||||
'\u2193', // ↓ 0x63 -> DOWN ARROW
|
||||
'\u2190', // ← 0x64 -> LEFT ARROW
|
||||
'\u2192', // → 0x65 -> RIGHT ARROW
|
||||
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
|
||||
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
|
||||
'\u2026', // … 0x68 -> ELLIPSIS
|
||||
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
|
||||
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
|
||||
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
|
||||
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
|
||||
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
|
||||
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
|
||||
'\ufffe', // 0x6F -> UTILITY TITLE BAR
|
||||
'\u00a9', // © 0x70 -> COPYRIGHT
|
||||
'\u2713', // ✓ 0x71 -> CHECKMARK
|
||||
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
|
||||
'\ufffe', // 0x73 -> TICK TRACK
|
||||
'\ufffe', // 0x74 -> TICK TRACK NUB
|
||||
'\ufffe', // 0x75 -> TAB CORNER
|
||||
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
|
||||
'\ufffe', // 0x77 -> CUSTOM 1
|
||||
'\ufffe', // 0x78 -> CUSTOM 2
|
||||
'\ufffe', // 0x79 -> CUSTOM 3
|
||||
'\ufffe', // 0x7A -> CUSTOM 4
|
||||
'\ufffe', // 0x7B -> CUSTOM 5
|
||||
'\ufffe', // 0x7C -> CUSTOM 6
|
||||
'\ufffe', // 0x7D -> CUSTOM 7
|
||||
'\ufffe', // 0x7E -> CUSTOM 8
|
||||
'\ufffe', // 0x7F -> CUSTOM 9
|
||||
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
|
||||
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
|
||||
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
|
||||
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
|
||||
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
|
||||
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
|
||||
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
|
||||
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
|
||||
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
|
||||
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
|
||||
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
|
||||
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
|
||||
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
|
||||
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
|
||||
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
|
||||
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
|
||||
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
|
||||
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
|
||||
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
|
||||
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
|
||||
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
|
||||
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
|
||||
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
|
||||
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
|
||||
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
|
||||
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
|
||||
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
|
||||
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
|
||||
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
|
||||
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
|
||||
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
|
||||
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
|
||||
'\ufffe', // 0xA0 -> REVERSED SPACE
|
||||
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
|
||||
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
|
||||
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
|
||||
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
|
||||
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
|
||||
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
|
||||
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
|
||||
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
|
||||
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
|
||||
'\ufffe', // 0xAA -> REVERSED ASTERISK
|
||||
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
|
||||
'\ufffe', // 0xAC -> REVERSED COMMA
|
||||
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
|
||||
'\ufffe', // 0xAE -> REVERSED FULL STOP
|
||||
'\ufffe', // 0xAF -> REVERSED SOLIDUS
|
||||
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
|
||||
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
|
||||
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
|
||||
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
|
||||
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
|
||||
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
|
||||
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
|
||||
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
|
||||
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
|
||||
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
|
||||
'\ufffe', // 0xBA -> REVERSED COLON
|
||||
'\ufffe', // 0xBB -> REVERSED SEMICOLON
|
||||
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
|
||||
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
|
||||
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
|
||||
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
|
||||
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
|
||||
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
|
||||
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
|
||||
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
|
||||
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
|
||||
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
|
||||
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
|
||||
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
|
||||
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
|
||||
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
|
||||
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
|
||||
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
|
||||
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
|
||||
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
|
||||
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
|
||||
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
|
||||
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
|
||||
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
|
||||
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
|
||||
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
|
||||
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
|
||||
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
|
||||
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
|
||||
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
|
||||
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
|
||||
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
|
||||
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
|
||||
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
|
||||
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
|
||||
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
|
||||
'\ufffe', // 0xDE -> REVERSED TILDE
|
||||
'\ufffe', // 0xDF -> RESERVED
|
||||
'\ufffe', // 0xE0 -> RESERVED
|
||||
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
|
||||
'\ufffe', // 0xE2 -> REVERSED UP ARROW
|
||||
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
|
||||
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
|
||||
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
|
||||
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
|
||||
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
|
||||
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
|
||||
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
|
||||
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
|
||||
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
|
||||
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
|
||||
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
|
||||
'\ufffe', // 0xEE -> MEMORY CHIP ICON
|
||||
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
|
||||
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
|
||||
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
|
||||
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
|
||||
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
|
||||
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
|
||||
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
|
||||
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
|
||||
'\ufffe', // 0xF7 -> CUSTOM 10
|
||||
'\ufffe', // 0xF8 -> CUSTOM 11
|
||||
'\ufffe', // 0xF9 -> CUSTOM 12
|
||||
'\ufffe', // 0xFA -> CUSTOM 13
|
||||
'\ufffe', // 0xFB -> CUSTOM 14
|
||||
'\ufffe', // 0xFC -> CUSTOM 15
|
||||
'\ufffe', // 0xFD -> CUSTOM 16
|
||||
'\ufffe', // 0xFE -> CUSTOM 17
|
||||
'\ufffe' // 0xFF -> CUSTOM 18
|
||||
)
|
||||
|
||||
// encoding: from unicode to C64 OS Screencodes (0-255)
|
||||
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
|
||||
|
||||
private fun replaceSpecial(chr: Char): Char =
|
||||
when(chr) {
|
||||
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||
else -> chr
|
||||
}
|
||||
|
||||
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||
val chr = replaceSpecial(chr3)
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> {
|
||||
if(chr.isISOControl())
|
||||
throw CharConversionException("no c64os character for char #${chr.code}")
|
||||
else
|
||||
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return try {
|
||||
Ok(text.map {
|
||||
try {
|
||||
encodeChar(it, lowercase)
|
||||
} catch (x: CharConversionException) {
|
||||
encodeChar(it, !lowercase)
|
||||
}
|
||||
})
|
||||
} catch(cx: CharConversionException) {
|
||||
Err(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(screencode.map {
|
||||
val code = it.toInt()
|
||||
if(code<0 || code>= decodingC64os.size)
|
||||
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
|
||||
decodingC64os[code]
|
||||
}.joinToString(""))
|
||||
} catch(ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
69
codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
Normal file
@ -0,0 +1,69 @@
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
object Cp437Encoding {
|
||||
val charset: Charset = Charset.forName("IBM437")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 255u
|
||||
'☺' -> 1u
|
||||
'☻' -> 2u
|
||||
'♥' -> 3u
|
||||
'♦' -> 4u
|
||||
'♣' -> 5u
|
||||
'♠' -> 6u
|
||||
'•' -> 7u
|
||||
'◘' -> 8u
|
||||
'○' -> 9u
|
||||
'◙' -> 10u
|
||||
'♂' -> 11u
|
||||
'♀' -> 12u
|
||||
'♪' -> 13u
|
||||
'♫' -> 14u
|
||||
'☼' -> 15u
|
||||
'►' -> 16u
|
||||
'◄' -> 17u
|
||||
'↕' -> 18u
|
||||
'‼' -> 19u
|
||||
'¶' -> 20u
|
||||
'§' -> 21u
|
||||
'▬' -> 22u
|
||||
'↨' -> 23u
|
||||
'↑' -> 24u
|
||||
'↓' -> 25u
|
||||
'→' -> 26u
|
||||
'←' -> 27u
|
||||
'∟' -> 28u
|
||||
'↔' -> 29u
|
||||
'▲' -> 30u
|
||||
'▼' -> 31u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> charset.encode(chr.toString())[0].toUByte()
|
||||
}
|
||||
}
|
||||
Ok(mapped)
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
package prog8.code.target
|
||||
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
|
||||
import prog8.code.target.cbm.AtasciiEncoding
|
||||
import prog8.code.target.cbm.IsoEncoding
|
||||
import prog8.code.target.cbm.PetsciiEncoding
|
||||
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
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.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return coded.fold(
|
||||
@ -23,12 +26,17 @@ object Encoder: IStringEncoding {
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
||||
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.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return decoded.fold(
|
||||
@ -36,4 +44,4 @@ object Encoder: IStringEncoding {
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
object IsoEncoding {
|
||||
val charset: Charset = Charset.forName("ISO-8859-15")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@ -27,11 +29,21 @@ object IsoEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object IsoEncoding: IsoEncodingBase("ISO-8859-15")
|
||||
object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5")
|
||||
object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16")
|
128
codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
Normal file
128
codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
Normal file
@ -0,0 +1,128 @@
|
||||
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, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
|
||||
'♥' -> 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>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package prog8.code.target.cbm
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
@ -7,7 +7,7 @@ import java.io.CharConversionException
|
||||
|
||||
object PetsciiEncoding {
|
||||
|
||||
// decoding: from Petscii/Screencodes (0-255) to unicode
|
||||
// decoding: from Petscii/Screencodes (0-255) to Unicode
|
||||
// character tables used from https://github.com/irmen/cbmcodecs2
|
||||
|
||||
private val decodingPetsciiLowercase = charArrayOf(
|
||||
@ -21,10 +21,10 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
'\u000e', // 0x0E -> SHIFT OUT
|
||||
'\ufffe', // 0x0F -> UNDEFINED
|
||||
'\ufffe', // 0x10 -> UNDEFINED
|
||||
@ -152,7 +152,7 @@ object PetsciiEncoding {
|
||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||
'\n' , // 0x8D -> LINE FEED
|
||||
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||
'\u000f', // 0x8E -> SHIFT IN
|
||||
'\ufffe', // 0x8F -> UNDEFINED
|
||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||
@ -283,7 +283,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
'\u000e', // 0x0E -> SHIFT OUT
|
||||
'\ufffe', // 0x0F -> UNDEFINED
|
||||
'\ufffe', // 0x10 -> UNDEFINED
|
||||
@ -411,7 +411,7 @@ object PetsciiEncoding {
|
||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||
'\n' , // 0x8D -> LINE FEED
|
||||
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||
'\u000f', // 0x8E -> SHIFT IN
|
||||
'\ufffe', // 0x8F -> UNDEFINED
|
||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||
@ -1061,6 +1061,7 @@ object PetsciiEncoding {
|
||||
'}' -> '├'
|
||||
'|' -> '│'
|
||||
'\\' -> '╲'
|
||||
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||
else -> chr
|
||||
}
|
||||
|
||||
@ -1076,7 +1077,10 @@ object PetsciiEncoding {
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Petscii character for '${chr}' (${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}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1085,7 +1089,7 @@ object PetsciiEncoding {
|
||||
Ok(text.map {
|
||||
try {
|
||||
encodeChar(it, lowercase)
|
||||
} catch (x: CharConversionException) {
|
||||
} catch (_: CharConversionException) {
|
||||
encodeChar(it, !lowercase)
|
||||
}
|
||||
})
|
||||
@ -1113,13 +1117,18 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Screencode character for '${chr}' (${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}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1128,7 +1137,7 @@ object PetsciiEncoding {
|
||||
Ok(text.map {
|
||||
try {
|
||||
encodeChar(it, lowercase)
|
||||
} catch (x: CharConversionException) {
|
||||
} catch (_: CharConversionException) {
|
||||
encodeChar(it, !lowercase)
|
||||
}
|
||||
})
|
||||
@ -1150,16 +1159,16 @@ object PetsciiEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
||||
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
|
||||
val code: UInt = when {
|
||||
petscii_code <= 0x1fu -> petscii_code + 128u
|
||||
petscii_code <= 0x3fu -> petscii_code.toUInt()
|
||||
petscii_code <= 0x5fu -> petscii_code - 64u
|
||||
petscii_code <= 0x7fu -> petscii_code - 32u
|
||||
petscii_code <= 0x9fu -> petscii_code + 64u
|
||||
petscii_code <= 0xbfu -> petscii_code - 64u
|
||||
petscii_code <= 0xfeu -> petscii_code - 128u
|
||||
petscii_code == 255.toUByte() -> 95u
|
||||
petsciicode <= 0x1fu -> petsciicode + 128u
|
||||
petsciicode <= 0x3fu -> petsciicode.toUInt()
|
||||
petsciicode <= 0x5fu -> petsciicode - 64u
|
||||
petsciicode <= 0x7fu -> petsciicode - 32u
|
||||
petsciicode <= 0x9fu -> petsciicode + 64u
|
||||
petsciicode <= 0xbfu -> petsciicode - 64u
|
||||
petsciicode <= 0xfeu -> petsciicode - 128u
|
||||
petsciicode == 255.toUByte() -> 95u
|
||||
else -> return Err(CharConversionException("petscii code out of range"))
|
||||
}
|
||||
if(inverseVideo) {
|
@ -1,57 +0,0 @@
|
||||
package prog8.code.target.virtual
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.CpuType
|
||||
import prog8.code.core.IMachineDefinition
|
||||
import prog8.code.core.Zeropage
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
|
||||
override var ESTACK_LO = 0u // not actually used
|
||||
override var ESTACK_HI = 0u // not actually used
|
||||
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return listOf("syslib")
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
// to not have external module dependencies we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(filename.endsWith(".p8virt")) {
|
||||
vm.runProgram(programNameWithPath.readText(), true)
|
||||
} else if(File("$filename.p8virt").isFile) {
|
||||
val source = File("$filename.p8virt").readText()
|
||||
vm.runProgram(source, true)
|
||||
}
|
||||
else
|
||||
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {}
|
||||
|
||||
override val opcodeNames = emptySet<String>()
|
||||
}
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(source: String, throttle: Boolean)
|
||||
}
|
68
codeCore/src/prog8/code/target/zp/C128Zeropage.kt
Normal file
68
codeCore/src/prog8/code/target/zp/C128Zeropage.kt
Normal file
@ -0,0 +1,68 @@
|
||||
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 && 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()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package prog8.code.target.c64
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
@ -6,7 +6,7 @@ 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, must be B1+1
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
free.removeAll(arrayOf(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(listOf(
|
||||
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,
|
||||
@ -43,8 +43,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
// remove the zeropage locations used for floating point operations from the free list
|
||||
free.removeAll(listOf(
|
||||
0x03, 0x04, 0x10, 0x11, 0x12,
|
||||
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,
|
||||
@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
).map{it.toUInt()})
|
||||
}
|
||||
|
||||
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||
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(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||
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
|
||||
@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
@ -75,20 +75,22 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
|
||||
allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
free.remove((4+reg*2).toUInt())
|
||||
free.remove((5+reg*2).toUInt())
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package prog8.code.target.cx16
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
@ -6,7 +6,7 @@ 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, must be B1+1
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||
|
||||
@ -40,30 +40,29 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
61
codeCore/src/prog8/code/target/zp/ConfigurableZeropage.kt
Normal file
@ -0,0 +1,61 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class ConfigurableZeropage(
|
||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||
basicsafe: List<UIntRange>,
|
||||
kernalsafe: List<UIntRange>,
|
||||
fullsafe: List<UIntRange>,
|
||||
options: CompilationOptions
|
||||
) : Zeropage(options) {
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||
if(address<=0xffu) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
codeCore/src/prog8/code/target/zp/PETZeropage.kt
Normal file
51
codeCore/src/prog8/code/target/zp/PETZeropage.kt
Normal file
@ -0,0 +1,51 @@
|
||||
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 && 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()
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation project(':compilerAst')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
47
codeGenCpu6502/build.gradle.kts
Normal file
47
codeGenCpu6502/build.gradle.kts
Normal file
@ -0,0 +1,47 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
setSrcDirs(listOf("$projectDir/src"))
|
||||
}
|
||||
resources {
|
||||
setSrcDirs(listOf("$projectDir/res"))
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
setSrcDirs(listOf("$projectDir/test"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
// Enable JUnit 5 (Gradle 4.6+).
|
||||
useJUnitPlatform()
|
||||
|
||||
// Always run tests, even when nothing changed.
|
||||
dependsOn("cleanTest")
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
@ -4,13 +4,17 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +1,57 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclType
|
||||
import prog8.code.core.IMachineDefinition
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.ICompilationTarget
|
||||
|
||||
|
||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||
|
||||
|
||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
|
||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
||||
|
||||
var numberOfOptimizations = 0
|
||||
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||
var mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeIncDec(linesByFour)
|
||||
mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeCmpSequence(linesByFour)
|
||||
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeStoreLoadSame(linesByFour, machine, program)
|
||||
mods = optimizeUselessPushPopStack(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods= optimizeJsrRts(linesByFour)
|
||||
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)
|
||||
@ -52,22 +59,20 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
}
|
||||
|
||||
var linesByFourteen = getLinesBy(lines, 14)
|
||||
mods = optimizeSameAssignments(linesByFourteen, machine, program)
|
||||
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeSamePointerIndexing(linesByFourteen, machine, program)
|
||||
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
// TODO more assembly peephole optimizations
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
@ -76,60 +81,51 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
|
||||
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
|
||||
|
||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||
if(modification.remove)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
if(modification.remove) {
|
||||
if(modification.removeLabel)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else {
|
||||
val line = lines[modification.lineIndex]
|
||||
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else if (haslabel(line)) {
|
||||
val label = keeplabel(line)
|
||||
if (label.isNotEmpty())
|
||||
lines[modification.lineIndex] = label
|
||||
else
|
||||
lines.removeAt(modification.lineIndex)
|
||||
} else lines.removeAt(modification.lineIndex)
|
||||
}
|
||||
}
|
||||
else
|
||||
lines[modification.lineIndex] = modification.replacement!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun haslabel(line: String): Boolean {
|
||||
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
|
||||
}
|
||||
|
||||
private fun keeplabel(line: String): String {
|
||||
if(':' in line)
|
||||
return line.substringBefore(':') + ':'
|
||||
val splits = line.split('\t', ' ', limit=2)
|
||||
return if(splits.size>1) splits[0] + ':' else ""
|
||||
}
|
||||
|
||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// when statement (on bytes) generates a sequence of:
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// beq check_prog8_s72choice_32
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// beq check_prog8_s73choice_33
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||
lines[1].value.trim().startsWith("cmp ") &&
|
||||
lines[2].value.trim().startsWith("beq ") &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||
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 optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||
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...
|
||||
@ -154,8 +150,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
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, program)
|
||||
val address2 = getAddressArg(second, program)
|
||||
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))
|
||||
@ -168,7 +164,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
val secondvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue) {
|
||||
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
||||
val address = getAddressArg(first, program)
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
@ -251,7 +247,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
val thirdvalue = third.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||
val address = getAddressArg(first, program)
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
@ -275,7 +271,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
val firstvalue = first.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==thirdvalue) {
|
||||
val address = getAddressArg(first, program)
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
@ -295,7 +291,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||
val address = getAddressArg(first, program)
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
val reg2 = second[2]
|
||||
@ -308,26 +304,15 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
/*
|
||||
sta A ; or stz double store, remove this first one
|
||||
sta A ; or stz
|
||||
*/
|
||||
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
|
||||
if(first[2]==second[2]) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
if(firstvalue==secondvalue) {
|
||||
val address = getAddressArg(first, program)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
|
||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||
// if Y isn't modified in between we can omit the second LDY:
|
||||
@ -364,29 +349,60 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
|
||||
mods.add(Modification(lines[4].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
beq +
|
||||
lda #1
|
||||
+
|
||||
[ optional: label_xxxx_shortcut line here]
|
||||
beq label_xxxx_shortcut / bne label_xxxx_shortcut
|
||||
or *_afterif labels.
|
||||
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
val autoLabelPrefix = 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: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
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 ")) ||
|
||||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||
(first.startsWith("ldx ") && second.startsWith("ldx "))
|
||||
) {
|
||||
val third = lines[3].value.trimStart()
|
||||
val attemptRemove =
|
||||
if(third.isBranch()) {
|
||||
// a branch instruction follows, we can only remove the load instruction if
|
||||
@ -397,7 +413,7 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
|
||||
}
|
||||
else {
|
||||
// no branch instruction follows, we can remove the load instruction
|
||||
val address = getAddressArg(lines[2].value, program)
|
||||
val address = getAddressArg(lines[2].value, symbolTable)
|
||||
address==null || !machine.isIOAddress(address)
|
||||
}
|
||||
|
||||
@ -423,23 +439,30 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
|
||||
} else if(first=="phx" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txa"))
|
||||
} else if(first=="phx" && second=="ply") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txy"))
|
||||
} else if(first=="phy" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tya"))
|
||||
} else if(first=="phy" && second=="plx") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tyx"))
|
||||
}
|
||||
|
||||
|
||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||
|
||||
private fun getAddressArg(line: String, program: Program): UInt? {
|
||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||
// try to get the constant value address, could return null if it's a symbol instead
|
||||
val loadArg = line.trimStart().substring(3).trim()
|
||||
return when {
|
||||
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
||||
@ -450,22 +473,18 @@ private fun getAddressArg(line: String, program: Program): UInt? {
|
||||
val identMatch = identifierRegex.find(loadArg)
|
||||
if(identMatch!=null) {
|
||||
val identifier = identMatch.value
|
||||
val decl = program.toplevelModule.lookup(identifier.split(".")) as? VarDecl
|
||||
if(decl!=null) {
|
||||
when(decl.type){
|
||||
VarDeclType.VAR -> null
|
||||
VarDeclType.CONST,
|
||||
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
|
||||
}
|
||||
when (val symbol = symbolTable.flat[identifier]) {
|
||||
is StConstant -> symbol.value.toUInt()
|
||||
is StMemVar -> symbol.address
|
||||
else -> null
|
||||
}
|
||||
else null
|
||||
} else null
|
||||
}
|
||||
else -> loadArg.substring(1).toUIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
@ -482,20 +501,269 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// jmp Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[0].value
|
||||
val second = lines[1].value
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
}
|
||||
else if (" rts" in first || "\trts" in first) {
|
||||
if (" jmp" in second || "\tjmp" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bra" in second || "\tbra" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bcc" in second || "\tbcc" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bcs" in second || "\tbcs" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" beq" in second || "\tbeq" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bne" in second || "\tbne" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bmi" in second || "\tbmi" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bpl" in second || "\tbpl" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bvs" in second || "\tbvs" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
else if (" bvc" in second || "\tbvc" in second)
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
|
||||
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
|
||||
(" 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only remove bra followed by jmp or jmp followed by bra
|
||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
when (first) {
|
||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
"pha" if 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
|
||||
}
|
||||
|
@ -1,61 +1,32 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.ast.expressions.ArrayIndexedExpression
|
||||
import prog8.ast.expressions.BuiltinFunctionCall
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.core.Cx16VirtualRegisters
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
|
||||
|
||||
fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
|
||||
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 (X last), except A,
|
||||
// 4) CPU Carry status flag
|
||||
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
|
||||
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
|
||||
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
|
||||
// 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.second.registerOrPair in pairedRegisters }
|
||||
val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A }
|
||||
val (regA, rest) = args4.partition { it.value.second.registerOrPair != null }
|
||||
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 }
|
||||
regsWithoutA.forEach {
|
||||
if(it.value.second.registerOrPair != RegisterOrPair.X)
|
||||
order += it.index
|
||||
}
|
||||
regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { 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 }
|
||||
regA.forEach { order += it.index }
|
||||
require(order.size==sub.parameters.size)
|
||||
|
||||
return order
|
||||
}
|
||||
|
||||
fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>,
|
||||
paramRegisters: List<RegisterOrStatusflag>): Boolean {
|
||||
fun isClobberRisk(expr: Expression): Boolean {
|
||||
when (expr) {
|
||||
is ArrayIndexedExpression -> {
|
||||
return paramRegisters.any {
|
||||
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||
}
|
||||
}
|
||||
is BuiltinFunctionCall -> {
|
||||
if (expr.name == "lsb" || expr.name == "msb")
|
||||
return isClobberRisk(expr.args[0])
|
||||
if (expr.name == "mkword")
|
||||
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
|
||||
return !expr.isSimple
|
||||
}
|
||||
else -> return !expr.isSimple
|
||||
}
|
||||
}
|
||||
|
||||
return args.size>1 && args.any { isClobberRisk(it) }
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user