mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
2411 Commits
Author | SHA1 | Date | |
---|---|---|---|
f874942075 | |||
2cadb546d5 | |||
344a1b9eb8 | |||
3c77f8a020 | |||
8e00408e3e | |||
abcdfd8e28 | |||
b0f5b6925d | |||
ef79d0c43e | |||
78b4288005 | |||
680f5d21ee | |||
c71aa0895f | |||
9f8e61789a | |||
932035cdc5 | |||
ef198f1493 | |||
48ef856c0b | |||
9aea2b22c4 | |||
e0055bc431 | |||
9553248ed6 | |||
39d2194d8f | |||
0800033b47 | |||
64d8943b7d | |||
444e97b00b | |||
1816bda7ea | |||
d4a2031c07 | |||
8cf0b6cf51 | |||
f2010bf7a5 | |||
8f56a7fe69 | |||
64c132ee0a | |||
84a7e86fe3 | |||
a8c09d6144 | |||
87c46ba730 | |||
0f83dc6491 | |||
cc22861719 | |||
a14c192ea3 | |||
b3d98be862 | |||
43027a4728 | |||
03831a7394 | |||
fdbbd181ea | |||
69075376dc | |||
504d1440cc | |||
9e33b8b8da | |||
0cfcc5cd29 | |||
e0de662f8e | |||
66a836d094 | |||
80095f4962 | |||
828d83dbef | |||
7de665d1e4 | |||
0a356ba73a | |||
41de8caa13 | |||
968609d06d | |||
3b199a2a87 | |||
0c1018ec61 | |||
bc3f2db3de | |||
06bedb7adb | |||
45a9751217 | |||
e8da62aa29 | |||
ddb2ff4216 | |||
f27e3478b9 | |||
38dc7fb7bd | |||
aa4cd13c31 | |||
4a4b6c04a1 | |||
37fa3b34a2 | |||
f8084e7955 | |||
4d5119ce3e | |||
d85c347a6c | |||
7dd758a753 | |||
806654fc44 | |||
8e6b91cb9e | |||
334e6dca28 | |||
f2daa17b92 | |||
6d9fccacb1 | |||
37638e7ed0 | |||
8a0e650511 | |||
8ba5a0d90c | |||
bfd3edb617 | |||
56ba24962c | |||
19a2110ba2 | |||
242a3eec63 | |||
fee46f2e54 | |||
6aed7e429a | |||
517ea82b99 | |||
99c29343de | |||
892fa76883 | |||
d446b57d05 | |||
0e086d788b | |||
498841d45d | |||
d1f8ee1e56 | |||
07feb5c925 | |||
75fd263e85 | |||
1e1f444cab | |||
89cc7e5fa9 | |||
265e7aefbf | |||
1c55a6c6dc | |||
8f18b5b8a7 | |||
f790182f0b | |||
813007a5d8 | |||
d03ff1e4d0 | |||
932bbd0381 | |||
01bd648cb2 | |||
779a5606a7 | |||
ccc11e49d2 | |||
d28c994ecd | |||
5d88717f32 | |||
e35cfd4971 | |||
bcc4bf5c2b | |||
a0594cbce3 | |||
078bfefe41 | |||
9c1b11d605 | |||
44d82f9190 | |||
37fcde30d6 | |||
09c6cb4d6b | |||
b428343c2a | |||
dfce292294 | |||
2b8f613a00 | |||
2eb137618e | |||
4bb2b8ca9b | |||
5179562fb2 | |||
0a4de45453 | |||
ffdc658dc8 | |||
7530f4407b | |||
73864c8101 | |||
f948917124 | |||
0d44492086 | |||
38a22fbc99 | |||
8ae435549d | |||
9b113c0cbb | |||
0e0fac8c4b | |||
4cd9bb8f99 | |||
ad9eaeafeb | |||
6cd392909c | |||
49ec430592 | |||
09f3fbeb38 | |||
e7698686fa | |||
66d939df0d | |||
6bc079c7b7 | |||
299419917e | |||
69f6afe420 | |||
b7279a3d9e | |||
e14b854d7b | |||
8bd7c601c0 | |||
997288fa03 | |||
0f26b39997 | |||
ae66fcac1e | |||
43944a94eb | |||
eba0bde6f3 | |||
4544af441b | |||
a8be94de6b | |||
b24df31c2b | |||
332ba8ed7e | |||
58400f53bc | |||
01c2112881 | |||
a546c2247d | |||
0da9142009 | |||
796add0ee2 | |||
00b32f64e6 | |||
f97b3f23e2 | |||
08a079a96e | |||
e98e951834 | |||
2668bf8519 | |||
dd4c073e18 | |||
c7c72f00c7 | |||
ef1c665b9a | |||
d56565be25 | |||
e076b3aedc | |||
ae3b2ddf5f | |||
1bdc427d73 | |||
6a639ce533 | |||
d91ca8b197 | |||
a01c0a283d | |||
5c393091a0 | |||
01b680504b | |||
8e4319cd5a | |||
5a776dd690 | |||
cce08d95db | |||
28c1b208c1 | |||
3844bf1f72 | |||
745d192563 | |||
ee782e92ac | |||
afbc91d1fc | |||
f998888d6d | |||
7d8b42d63e | |||
6ebd4e821f | |||
d1806bfdc3 | |||
1d2d7155da | |||
b09e0a05bf | |||
c609e982fe | |||
2b227b43fe | |||
48f09f71ab | |||
ead8c59bda | |||
db52a9466c | |||
1509de390e | |||
88a1aa4f3d | |||
172e78e8f2 | |||
36bfef567d | |||
e40ebd75a2 | |||
992732f2cb | |||
b58a3ba1bb | |||
afe521b0c9 | |||
5d9caef45f | |||
278e2f5605 | |||
1e299bf360 | |||
8dfa0bc38c | |||
fde136fb7b | |||
ee4da1a757 | |||
ae2d96c455 | |||
6d8fbe0877 | |||
2fa1d8f2e8 | |||
533090a68e | |||
1dff59e1d6 | |||
44d232f52a | |||
5f6cff739a | |||
2764d235a9 | |||
45debff89f | |||
c45fbe6310 | |||
9ef9c24388 | |||
6a40f23578 | |||
6a0a6b4751 | |||
0bee6f6b41 | |||
82a15b5a16 | |||
11b7c4459e | |||
98570ac456 | |||
1b2296ad5b | |||
16851746d6 | |||
935450a45f | |||
ba67fd318b | |||
08ac459a41 | |||
a83e9d9a0a | |||
62d3f01948 | |||
af5ca2d0b8 | |||
ab4bcdf12d | |||
a6756d2cea | |||
f81061dd42 | |||
8e2c304b3c | |||
f21adaa3ef | |||
2637939e62 | |||
faf05582f8 | |||
161c02ced3 | |||
ff8de8e42d | |||
09d506194f | |||
42db3085df | |||
ad14c88fde | |||
0c9daf6eaf | |||
86c6530e46 | |||
159f80d629 | |||
aa949165c7 | |||
d22359b6e7 | |||
d73709653d | |||
405926e811 | |||
36758f41a4 | |||
7ebc9c79cf | |||
e0668b55b9 | |||
76c09da961 | |||
7e3b8c2c59 | |||
ecca854c7c | |||
3b0d7ea960 | |||
f70fa42eac | |||
5698de6cf4 | |||
c5a333a904 | |||
ff324955dd | |||
70436f5dca | |||
31177a2b1b | |||
4de012fc49 | |||
ee2888e744 | |||
efe4df92dc | |||
723ab54f97 | |||
d9389afc66 | |||
e7178ee496 | |||
d5f35bb3fb | |||
72f1a779f2 | |||
3277544295 | |||
98d2c64d5d | |||
f68b46fc60 | |||
d54ab856e7 | |||
16b24fadea | |||
b3803cbdf1 | |||
2ceaa25181 | |||
513611c5a6 | |||
7ec4ba40ad | |||
92374e122b | |||
94f12732ab | |||
0904712a00 | |||
32becdbced | |||
34aa21f7d9 | |||
cc81dd7d3e | |||
335213b55f | |||
13ab4166c0 | |||
3dc5a0e7f8 | |||
e15c5cde53 | |||
d88c09b098 | |||
893b383bdf | |||
dd7c9d62e6 | |||
97c5c90eff | |||
1fb94e7a7b | |||
daca87c6d0 | |||
203ec5fa46 | |||
9ea69c07b8 | |||
68539d6cc9 | |||
f75fd0811e | |||
836bc9d456 | |||
a37769aafe | |||
68e62e4bd2 | |||
a5cd3728c9 | |||
a48ce35f0b | |||
e1835b5775 | |||
433832b329 | |||
ee81da14d6 | |||
6395d1908e | |||
989a5a2f8a | |||
b7a622c68e | |||
a8507b437d | |||
e505bf9ccf | |||
a289b32053 | |||
c3f1f09ad1 | |||
70ee2026ff | |||
690782bf60 | |||
755cc4835e | |||
a684ea46e4 | |||
8fbe13f99d | |||
452e9e275f | |||
cd40088636 | |||
9b9e6f4af5 | |||
ae6eeadf54 | |||
5268b05060 | |||
390263a34e | |||
55646edc3e | |||
8d177beb78 | |||
1da0c59182 | |||
36e8f10d2b | |||
cdf5a8f20f | |||
eb64d92333 | |||
eb55da63ef | |||
918302f79b | |||
9d7131d9f6 | |||
229c1114dd | |||
885df9156f | |||
c319233ddc | |||
958b5c0780 | |||
880c0a5da8 | |||
237c6dc856 | |||
ccf6e32bf9 | |||
a1874f6f00 | |||
95e4490a8a | |||
31c132c2eb | |||
00b0ec58b4 | |||
a1d0e5bb65 | |||
03e0d4b2e8 | |||
6afdd4e6fd | |||
b500a0d477 | |||
dd2463a440 | |||
23a8bebd9e | |||
3caf9108ad | |||
bde4be8231 | |||
0bbbb12ed2 | |||
b570bdaed7 | |||
8c0843cc87 | |||
31458ffd81 | |||
c15c10a94e | |||
9fca978725 | |||
b125901717 | |||
eb018ae660 | |||
7e5a9474fe | |||
525a9b5036 | |||
c3fbdf34ca | |||
48bd51e1a5 | |||
10d0b03a90 | |||
e1b3582f08 | |||
95be1c9e22 | |||
1ce8fe06d5 | |||
15c649024e | |||
e97303c226 | |||
3b786c819d | |||
04959dbd8b | |||
5cd4b874ea | |||
f14ea1b3de | |||
9cc0cda0fb | |||
09a7a4bbe5 | |||
cfea8b3745 | |||
28bf0b61ce | |||
2dc2429735 | |||
83d4592526 | |||
2d528c26ae | |||
66b3dce794 | |||
93f77a1045 | |||
aa4d23a3d5 | |||
2d7ebff8e9 | |||
bad9dd3b3b | |||
2f4e517857 | |||
ff35ba3696 | |||
72768e7fad | |||
77f3852cdc | |||
66857ca477 | |||
75514fc7af | |||
be06d871b6 | |||
f98ee326b4 | |||
bc8126eb16 | |||
4c8beefdcb | |||
bbb6c53457 | |||
d8991894e3 | |||
c7b7dcfd03 | |||
2c9e50873c | |||
923367296d | |||
151a206617 | |||
e403c4cf99 | |||
e3fbe37f9f | |||
dc870cd5ea | |||
584be44743 | |||
5fffd35ec1 | |||
b92e22e4a6 | |||
3e6d16a7a8 | |||
ecbcc277b8 | |||
dff1d9e4dd | |||
7c0bde7310 | |||
a82d21ac05 | |||
0bf8378fcb | |||
017ef8a837 | |||
0d63cdcb96 | |||
68a6f99c9f | |||
60781bcfc4 | |||
77fa2e2722 | |||
c36afd872e | |||
7e58a4c130 | |||
19a4bf1088 | |||
9678bbae4b | |||
a4d093afa1 | |||
ba788bcf0f | |||
f2c62bee7e | |||
548721e306 | |||
1ae950a638 | |||
c9385e93fe | |||
9bb16e293c | |||
c223702ea0 | |||
9167ba499d | |||
2d7e95e1b6 | |||
0cba736446 | |||
0816a57032 | |||
a0ab0bd3e2 | |||
b89ad4b328 | |||
6cda76a116 | |||
c112b327ab | |||
46c12a8899 | |||
c5219dfb3f | |||
4a8ee6815a | |||
e1b6bb154a | |||
b19c282269 | |||
e520921746 | |||
970642244b | |||
3b90be2d9e | |||
2f756f1e3a | |||
78e84182f0 | |||
65a7a8caf8 | |||
4c6a2f5df9 | |||
fea297e409 | |||
7cf6aba625 | |||
3bbc00cc8c | |||
70ed2b4203 | |||
0adce9b9c6 | |||
0e781d18fa | |||
4575a8fffe | |||
10d0ff252b | |||
c7d54570cc | |||
7136b33f2e | |||
70a78e74f6 | |||
d5707b7bf3 | |||
9f247901d4 | |||
5659742d97 | |||
450eaf7c4a | |||
47485e4b49 | |||
64254e758d | |||
c1aa5d4e47 | |||
ab8173637a | |||
3841cef497 | |||
b717f1c7eb | |||
da57f76de3 | |||
4784f1c65a | |||
41af63b333 | |||
e2bb0de24d | |||
b791fae9ce | |||
6033a9e20c | |||
9e8c8973d8 | |||
3933bf5c1a | |||
708e296774 | |||
84925ab69c | |||
b3cb9b7fe2 | |||
9cb61fa34d | |||
7c219d235c | |||
6938c79f88 | |||
b8284a147d | |||
15ee90e99c | |||
795f80b4ec | |||
6b6427492d | |||
6055b8c3dc | |||
a98cb50d55 | |||
e98bbc1c52 | |||
7245aece4f | |||
60cbb02822 | |||
4e863ecdac | |||
5037033fcf | |||
e6b158bc97 | |||
4cc0dfa10b | |||
4ced8889d3 | |||
d26967a87d | |||
fc8955941b | |||
071a80360f | |||
d2154f5f2e | |||
334d382bfa | |||
90c4b00f74 | |||
71261525e8 | |||
3126959576 | |||
02e51d8282 | |||
ffb2027a19 | |||
70c9ab9074 | |||
6d1fdf1ba6 | |||
1f7180d9a8 | |||
b4e94ae4dd | |||
07c606bfc9 | |||
e705a8bd89 | |||
b3bdfb7f1f | |||
5af1aeb092 | |||
be64fa674a | |||
204f5591a9 | |||
ee3e3a3a40 | |||
f9200a2b75 | |||
f570b70827 | |||
0db141eeac | |||
acb2ee53bb | |||
c544b7f5ba | |||
c0024e97e5 | |||
bdf8aa9168 | |||
de5ce0f515 | |||
bb95484c8a | |||
cad18b8a3a | |||
0f6a98751a | |||
aac5a4c27f | |||
d3f6415387 | |||
04da44eb98 | |||
7649be97b1 | |||
c9ef777e0f | |||
c0cb2438d5 | |||
30c531b39e | |||
bf703a8a66 | |||
e7b631b087 | |||
a9f5dc036c | |||
0a83b51e00 | |||
eab63ecc6c | |||
b0794cf35e | |||
5b9e71a27d | |||
eae41de27d | |||
e9163aa3a7 | |||
8c617515ba | |||
04e4e71f2e | |||
a587482edf | |||
0aac9350d5 | |||
f56c12ee4e | |||
4bb9ae61f2 | |||
ff7f3484e4 | |||
5da3abe6b4 | |||
c0b398e0ce | |||
3de10adac2 | |||
1b573d6552 | |||
2a96f93919 | |||
c6b2639ca4 | |||
b9abf37a7e | |||
373cbb4144 | |||
a521982576 | |||
a77fde577c | |||
ea6926e57d | |||
ba25b7fee6 | |||
7ee162d98b | |||
380f557c45 | |||
1bdae53f4e | |||
9314c346da | |||
bfaad1388c | |||
0b580ad05d | |||
bb35a80177 | |||
24fc95ac81 | |||
8f864417c4 | |||
bb9d29b061 | |||
b9d8ec1463 | |||
1842a7660d | |||
5caa2f5536 | |||
d6078be8b7 | |||
cf60723f14 | |||
f7ff0a2b1d | |||
cc49664b2f | |||
99fe74f026 | |||
b021869eeb | |||
b8806d163b | |||
1116aae1de | |||
5e5f60253b | |||
bbc02752c9 | |||
9896bc110e | |||
ca60f8ecdd | |||
544acd1e35 | |||
6e07602d77 | |||
82898f7bba | |||
d61283a8bc | |||
1ee3f826cc | |||
4a00a5ba9e | |||
39eda67867 | |||
a99d38fdaa | |||
0eb2d437e2 | |||
3ac9036c79 | |||
c94e292176 | |||
91d87c2d9b | |||
ff472f69c0 | |||
e18119e24c | |||
4a592dc64c | |||
d9e13201dd | |||
5c75b19c5d | |||
52a77db60f | |||
0513c250fb | |||
48864ad6cf | |||
cdbccad21e | |||
e15bc68c9b | |||
8bffd7672d | |||
61df5b3060 | |||
b5255444cd | |||
0c94e377fc | |||
8e5c67b4b2 | |||
b24f2f1756 | |||
c69c17de42 | |||
061617122a | |||
125ce3240f | |||
7215efe167 | |||
06d1570142 | |||
093c370faa | |||
aec9574737 | |||
7ceb76cff5 | |||
300e2fe9f8 | |||
91e1643627 | |||
91421b0c62 | |||
40f611664f | |||
dcba4f4098 | |||
c098ad2b3b | |||
b43223cb7a | |||
e243531dab | |||
1af38e62bc | |||
f37f062cdc | |||
7e734214dc | |||
05d152746f | |||
dea7f37553 | |||
415c599310 | |||
70cd4fedbe | |||
1e6d7673bc | |||
b4963b725b | |||
0371ffa4ce | |||
6a664a7e15 | |||
88ce9300bc | |||
85cf0e311c | |||
0e3d75cfeb | |||
630c8a5faa | |||
905921a684 | |||
1e469b3b0f | |||
bff3c4f95c | |||
bd2bcb6994 | |||
4c8898a639 | |||
97df33ab1a | |||
ef46fb2685 | |||
d5d6dd3614 | |||
6c233c6a0a | |||
6db715d879 | |||
ab02e8a546 | |||
8cbfe64f19 | |||
fd1e9971e4 | |||
68336a76c5 | |||
393e914a86 | |||
ffb54110e9 | |||
533d825f1a | |||
c65279b672 | |||
f9926beeef | |||
add8a777d8 | |||
21bc505d85 | |||
3fc49c001e | |||
3d69a95c49 | |||
d81fdf6d6b | |||
87d3109ffb | |||
180dbbb521 | |||
24aac7cee5 | |||
53e18a5387 | |||
92062d056d | |||
06368ab0a1 | |||
38efe25c68 | |||
319079de7a | |||
025bf900a5 | |||
2885f4f7b1 | |||
c07eda15b1 | |||
4274296cf3 | |||
76a203d4df | |||
24f37e2062 | |||
f465b2e2a0 | |||
ce00e49a89 | |||
d494f9d66b | |||
c35a183a64 | |||
9cdd5fe7f2 | |||
c21428215e | |||
64d5af46f5 | |||
25846ea18a | |||
798383596d | |||
9ca71bc937 | |||
5407429ec0 | |||
ee5c94f6db | |||
91045afbee | |||
3f64782023 | |||
f8d35f9502 | |||
ea78d3ec9a | |||
e056a28316 | |||
0bea721c2e | |||
e1b89494d0 | |||
cd8e7f3912 | |||
50604c25c2 | |||
aa6b2357d8 | |||
5b2d29bef6 | |||
a296d26328 | |||
d01a26ec61 | |||
efd7d6f0c0 | |||
b55be093be | |||
7c1d5cadd7 | |||
dd1592b03b | |||
9b37ac483f | |||
090820958e | |||
ac21e1be5c | |||
5196443b26 | |||
c8531cbeb1 | |||
c560abedba | |||
9b952fbc44 | |||
ccdf05e922 | |||
c3d74f2ae9 | |||
f47498888c | |||
5665a7f0cb | |||
b8178c6c8d | |||
4a0f15eb88 | |||
c4f53fe525 | |||
8c93ec52de | |||
befe0fff2a | |||
b6a837cbea | |||
4861973899 | |||
c593e4b500 | |||
5bf78c20d4 | |||
5c672130e6 | |||
d8214d4f12 | |||
64d1f09ce0 | |||
47d0f0ea40 | |||
2d85fd093e | |||
d936568b76 | |||
4598a83e8e | |||
f4bf00ad31 | |||
07fde7f6cc | |||
729209574e | |||
f28206d989 | |||
0c81b32cac | |||
11216017cb | |||
a7b9f53967 | |||
1fa2e2e37d | |||
f67d5faeb7 | |||
5cbf859458 | |||
629ed74d09 | |||
ca2af2ca63 | |||
52ab089615 | |||
01461a196d | |||
04832f052a | |||
c8b2c8ae50 | |||
1b81c7fb22 | |||
9ccda0247e | |||
a7df4dcf25 | |||
d91f47c791 | |||
a9ac4e7f44 | |||
fc3ec57437 | |||
266f6ab919 | |||
6218c1c00b | |||
cc81d6fe82 | |||
69f9102f2d | |||
beb9275982 | |||
abe48713f2 | |||
82cfaf2fbb | |||
3d3bc4738f | |||
2d0746f5a4 | |||
9c71e2f1c8 | |||
134fd62da8 | |||
2afd283582 | |||
c66734bab0 | |||
8e56a61f95 | |||
d265271148 | |||
b40e397b28 | |||
35ff1d996a | |||
deea0b05cb | |||
c50c9ca545 | |||
a819b4a5a5 | |||
df2d7d4734 | |||
79ce4098cf | |||
374464a1f8 | |||
c8d0bf27af | |||
6e4ae034b2 | |||
52b560e72d | |||
9b971ad222 | |||
3613162d09 | |||
3a272e998d | |||
d4c750beb4 | |||
84b31e65e1 | |||
7b802bfd3d | |||
f9c4632b8d | |||
e4764cd8a6 | |||
dd78a3a686 | |||
94c06e13f4 | |||
e8bebe5a75 | |||
5b0e1b4f9e | |||
8c0a93779b | |||
9241479da4 | |||
8ffca93cd5 | |||
7fea0c124a | |||
20dbdb20d2 | |||
e6b8e2e8be | |||
7c5b7f77cc | |||
de84547a21 | |||
44676756ae | |||
b399b0f182 | |||
1152191f48 | |||
af1b07ad44 | |||
b8113fff1e | |||
ff6948cf2d | |||
fd25e85d59 | |||
c07cd72e85 | |||
e2c101206c | |||
92276b5769 | |||
a2133f61a8 | |||
199adbbcf0 | |||
dc316fd7b4 | |||
025183602f | |||
db4619a9d9 | |||
451e527b7c | |||
54dd3a00df | |||
03c5dab79d | |||
1fdee861e8 | |||
c12bf991b3 | |||
78a097585d | |||
39132327cc | |||
dc32318cec | |||
592f74124c | |||
e5e63cc5ac | |||
f40e0f786d | |||
ebd9f1471b | |||
d76547ead4 | |||
4600772e05 | |||
ed597423cd | |||
f20ca06f85 | |||
a636d3f394 | |||
043df18daa | |||
96996bf18e | |||
f350137a14 | |||
b7a6f3ec75 | |||
6c34672549 | |||
e779a07bce | |||
9a36e8ba3b | |||
c968bacb01 | |||
25199dfb43 | |||
48fed4e6fb | |||
fc253237c9 | |||
589948c7f4 | |||
7e69690605 | |||
95f498ba9b | |||
fd07ae5225 | |||
8acd94fc89 | |||
1436480eab | |||
448d176c24 | |||
fd269453a4 | |||
b3b380964c | |||
6e9025ebf2 | |||
3922691b3c | |||
0545b77cf4 | |||
6b3f39fa1a | |||
3114ab87dc | |||
00bc99cc7b | |||
540b3ae2f4 | |||
dbfe4140e1 | |||
d3675ec254 | |||
ded2483fc0 | |||
e62ea388e0 | |||
f20356e9be | |||
d282a2d846 | |||
4641ac46e7 | |||
ba9268a09e | |||
fb9902c536 | |||
5318ba6c6e | |||
fd5ebef488 | |||
d9e4f39ddc | |||
435b9d8973 | |||
0ea70ba656 | |||
92a07b87d2 | |||
c3c82282ba | |||
adc15c24ef | |||
dddf9a9396 | |||
9ca6860ffa | |||
f7dd388954 | |||
6012839f0e | |||
8e9cbab053 | |||
aaf375a57b | |||
3cce985f03 | |||
c59df6ec20 | |||
5c3f41f64d | |||
cf3523f49f | |||
db794752cb | |||
bceaebe856 | |||
3916de2921 | |||
9e0f8c1a97 | |||
0cbc56b82e | |||
b95608f68a | |||
b6e5dbd06c | |||
914f19be86 | |||
f09bcf3fcf | |||
d0b18dec8e | |||
75d486b124 | |||
4914609485 | |||
75bd66326a | |||
8f904f75bb | |||
549c598f51 | |||
ed68d604d6 | |||
f83752f43b | |||
86c22636eb | |||
30d20a453b | |||
fe29d8a23f | |||
694d088160 | |||
6aabbffc62 | |||
7b59bc8d12 | |||
79d0fb0b52 | |||
edf56d34f8 | |||
623329fb33 | |||
9f0074eef9 | |||
6733253826 | |||
f117805129 | |||
c75b1581d2 | |||
109e118aba | |||
201b77d5b6 | |||
a5ca08f33d | |||
86210c4513 | |||
988a3e4446 | |||
0f5cd22bb7 | |||
2f5bed36b3 | |||
5b6534bb28 | |||
e31e5b2477 | |||
07d5fafe2e | |||
e08da659e5 | |||
8a4979f44c | |||
e67464325f | |||
94c9b0d23b | |||
e9ec310d8a | |||
c78d1e3c39 | |||
e94319145f | |||
3f3b01b5f6 | |||
19a2791c65 | |||
4e8ccf0ef3 | |||
f1a7d5ecf7 | |||
8b05abb80d | |||
48c9349ce9 | |||
117d848466 | |||
9a2df072cc | |||
99c62aab36 | |||
224278e07a | |||
74b69e191e | |||
8cda8a727c | |||
a3c0c7c96f | |||
4403e4ed62 | |||
9b209823f6 | |||
b2cb125bd4 | |||
5e8f767642 | |||
6ee270d9d8 | |||
44fa309d20 | |||
58d88f3dd4 | |||
e980c23177 | |||
75224321bb | |||
801af05b20 | |||
7611dbbddc | |||
6d40ca15bc | |||
32c1c19224 | |||
bbf6357222 | |||
dc16629c24 | |||
3718b9d768 | |||
c25eb088ec | |||
3feb3e52f8 | |||
8e730ef93d | |||
e0913a39ab | |||
7a27fbc001 | |||
ee0dbdad35 | |||
9225f88f89 | |||
a04839dd6b | |||
002006517a | |||
f5b202d438 | |||
a7df094ff4 | |||
1e6fa77633 | |||
eb4cff202c | |||
7ee777f405 | |||
81bd5c784e | |||
b526e132a7 | |||
1860f66de5 | |||
ded9ada9bc | |||
d0e6a2eb8b | |||
4e103a1963 | |||
475e927178 | |||
ca7932c4f0 | |||
8ab47d3321 | |||
def7e87151 | |||
27568c2bef | |||
0694a187d7 | |||
832601b36b | |||
578969c34c | |||
d1d0115aed | |||
c89e6ebfab | |||
ca1089b881 | |||
a1d04f2aad | |||
bf0604133c | |||
a82b2da16e | |||
f2273c0acc | |||
17bedac96c | |||
4831fad27a | |||
5e896cf582 | |||
add3491c57 | |||
f470576822 | |||
10760a53a8 | |||
eee805183c | |||
b8fb391022 | |||
3c698f1584 | |||
2fad52d684 | |||
ec64a68a71 | |||
db55562f6a | |||
d8409a9d2b | |||
0d0ce6eec1 | |||
483f313eda | |||
7b6c742178 | |||
d4a35ba6ff | |||
68b112837a | |||
e2f20ebf94 | |||
f870e4965a | |||
7ebcb219d6 | |||
c21913a66b | |||
77e956a29f | |||
08275c406a | |||
2931e1b87b | |||
153b422496 | |||
0f6a6d6fea | |||
91fdb3e2d4 | |||
d8e87bd881 | |||
922033c1b2 | |||
df1793efbf | |||
836a2700f2 | |||
8f3aaf77a1 | |||
00c059e5b1 | |||
f4f355c74a | |||
b465fc5aaf | |||
2d78eaa48d | |||
d08451bccc | |||
d8e785aed0 | |||
267b6f49b5 | |||
e6688f4b9d | |||
9d7b9771c2 | |||
136a9a39de | |||
3dcf628fdb | |||
e614e9787a | |||
e426fc0922 | |||
5d4bfffc7e | |||
207cdaf7a4 | |||
7315b581ce | |||
38efaae7b2 | |||
469e042216 | |||
0f1a4b9d8f | |||
7303c00296 | |||
fc55b34d84 | |||
6f67fc0e02 | |||
562d722ad5 | |||
144c1ba3a6 | |||
06b032af91 | |||
3603140114 | |||
e094785cbd | |||
e7408224ac | |||
e67c05c274 | |||
b22804efaf | |||
890f55f91a | |||
cc5fc0b892 | |||
5efe2b027a | |||
5b6569d0f9 | |||
0eda7ac498 | |||
a5ef353484 | |||
67a36d8d31 | |||
7cc3cc3990 | |||
dc0edc4c2b | |||
71d2f091e5 | |||
c2f062a391 | |||
224f490455 | |||
5b35232ab4 | |||
6d6db70e42 | |||
6830e15b4e | |||
3f07cad35d | |||
e951340033 | |||
db8912a735 | |||
0e297731a3 | |||
f20c4f98ac | |||
05e60cc7c0 | |||
55b4469767 | |||
f15516e478 | |||
17ceadbadf | |||
8c25b2b316 | |||
8b1ae404a3 | |||
13534cd4a9 | |||
abfb345503 | |||
42ae935496 | |||
434515d957 | |||
094f7803b7 | |||
b0c7bad391 | |||
e9a4a905ef | |||
7b6cd0cfbe | |||
b718b12083 | |||
cfa7258ff4 | |||
b70e0a0870 | |||
da8eb464b8 | |||
8f9d1cfa30 | |||
585009ac5c | |||
30ee65fd14 | |||
76428b16f0 | |||
0d7b14e2d8 | |||
a9d19d02b3 | |||
adcbe55307 | |||
aa99a7df64 | |||
00afa1ce52 | |||
e94bf4c63c | |||
ec5adffdc2 | |||
733c17ad3a | |||
53b0b562e6 | |||
fabae6e970 | |||
a9f9c40d8a | |||
6fc89607d3 | |||
2340760f53 | |||
39d6d2857e | |||
7b722a0001 | |||
e7682119e0 | |||
af6be44676 | |||
5a8f97a0b6 | |||
0d4dd385b8 | |||
94f0f3e966 | |||
43e31765e5 | |||
7c1bdfe713 | |||
9f09784b55 | |||
e7a3a89bfb | |||
7ea7e63f44 | |||
1d2ce2cbeb | |||
06cf2e0bd7 | |||
9d219ae4b9 | |||
71f5a6c50e | |||
90b2be2bf4 | |||
db1aa8fcbd | |||
11c000f764 | |||
4d6dcbd173 | |||
0da117efd2 | |||
533c368e32 | |||
8883513b0e | |||
dcc9a71455 | |||
1a56743bb1 | |||
387a4b7c35 | |||
1d65d63bd9 | |||
dda19c29fe | |||
ca41669f4f | |||
0e1886e6bd | |||
c26e116f0e | |||
5c9c7f2c5e | |||
ca2fb6cef3 | |||
46dac909ef | |||
b1e4347e10 | |||
97aa91c75e | |||
4f8fb32136 | |||
e0fbce0087 | |||
fb22f78fb3 | |||
d6393cdbe5 | |||
5167fdb3f0 | |||
ab00822764 | |||
b4352ad38b | |||
d07d00fa41 | |||
11d87e4725 | |||
627ed51a1b | |||
c8f3bfa726 | |||
3091e3a1c8 | |||
2f3e7d1c27 | |||
0e831d4b92 | |||
7294ec9a3c | |||
e34bab9585 | |||
7dd14955c1 | |||
6428ced157 | |||
30a42ec1bd | |||
aacea3e9db | |||
6886b61186 | |||
0744c9fa29 | |||
502a665ffc | |||
3c315703c0 | |||
12ed07a607 | |||
101b33c381 | |||
97f4316653 | |||
b0704e86f0 | |||
a182b13e5a | |||
80b630a1e4 | |||
475efbe007 | |||
3ab5e5ac48 | |||
c6c5ff2089 | |||
176ec8ac7d | |||
dcdd4b3255 | |||
fc0a0105b3 | |||
f3960d21a8 | |||
a44d853c1b | |||
6b41734d6a | |||
c33dc0f3be | |||
bb5ffb24a8 | |||
a878c9a61d | |||
6454bf8ec4 | |||
40aa733ea7 | |||
f37a822725 | |||
f249ccd414 | |||
7ef4ddf0f3 | |||
d8e18df3a1 | |||
78d3d9d27d | |||
0aa0ec5abd | |||
b6eef3612f | |||
666d62dd7a | |||
44ee4b989f | |||
18790d867c | |||
d6b8936376 | |||
4d840c7db8 | |||
4d2b21816d | |||
2d34fdd28f | |||
68abda1219 | |||
f778f08f76 | |||
ac1bd2fb7b | |||
4b7b1379d9 | |||
e560e2ab3f | |||
1e441c2ddf | |||
93ce74eeb1 | |||
f718f4251b | |||
4644c9b621 | |||
197081f10d | |||
00b717cde8 | |||
34aa917ca4 | |||
a38ddcb364 | |||
5b9576df4e | |||
310219e5d7 | |||
a0deb463c9 | |||
90ddec2ad8 | |||
f6b03d5a78 | |||
f531daa872 | |||
046dceb5c2 | |||
dcc1f00048 | |||
05f935b598 | |||
f2d27403c5 | |||
473efbe67a | |||
aeabf0f324 | |||
80ab552ad8 | |||
7d4695c5b2 | |||
5189eaca36 | |||
cfb31377fc | |||
a07c52e112 | |||
8e1071aa89 | |||
7cb9a6ba60 | |||
350dc731f1 | |||
f690f58bd4 | |||
4bc65e9ef7 | |||
2d600da8b6 | |||
35af53828a | |||
10ddd5b127 | |||
f46e131f18 | |||
feb5c8be95 | |||
edf12bec71 | |||
ff1fc28287 | |||
314398ba4c | |||
840331347b | |||
6181b12ab8 | |||
68da661edc | |||
88cbb6913d | |||
7a26646e1b | |||
92eb3b0bf6 | |||
fb63434eee | |||
97f90d9684 | |||
f91786367f | |||
6a57337a68 | |||
211e2bb37a | |||
d2d08bf143 | |||
8acb37b6c2 | |||
81b3d2db4f | |||
9633c0b07a | |||
1dfa8ee7d8 | |||
1163543a98 | |||
bdb7de34be | |||
9500fc11ac | |||
65daf29acd | |||
298b25cf7d | |||
41f4e22a17 | |||
288c57c144 | |||
7ff8923569 | |||
b41779bd02 | |||
beea6bc794 | |||
fee58e98c5 | |||
c51c1da618 | |||
ea2812f50f | |||
3ec05709d5 | |||
4bdac7404a | |||
cc41218d37 | |||
4b336b1853 | |||
e1c77ce236 | |||
064d412ec8 | |||
7fff4f249d | |||
7a3745f642 | |||
f8658f6afa | |||
223b725a10 | |||
25aad8d7be | |||
b2c9b7635d | |||
24d13dd120 | |||
965340ff90 | |||
8e36fe6bef | |||
2eb41a8caf | |||
fb989ae62f | |||
7901ec2a64 | |||
f675dbc726 | |||
2ad4fdbbb9 | |||
97cb0cbd08 | |||
4ca0805de1 | |||
4b358abbb7 | |||
dc82a0fc16 | |||
435d6f6f3f | |||
ef92451d1a | |||
06184bdcb1 | |||
af98d01053 | |||
bb1cda0916 | |||
a6d0ea347c | |||
0fcd57192b | |||
a6ffa5738b | |||
c75bd97537 | |||
eea09f4de5 | |||
5656ec11d3 | |||
eb53e44cb0 | |||
69f3106062 | |||
8ab99f6129 | |||
53a3c59a91 | |||
df36983049 | |||
bda016bb3b | |||
cc174b7b85 | |||
bf9d120081 | |||
775c85fc18 | |||
5a756aaed9 | |||
dca092fd7c | |||
c6e92ecac4 | |||
93008ff605 | |||
43c7b935df | |||
8f9a0a244a | |||
fd13bd864e | |||
710f27afa9 | |||
f537793b0b | |||
f7183e38ee | |||
0a65dfdd10 | |||
3075578245 | |||
b042b7705e | |||
d56eb397f9 | |||
3054a1d32d | |||
0a3cd652b0 | |||
f70b914779 | |||
46ca0ac10d | |||
031f647952 | |||
8f1c86f550 | |||
926fdecd13 | |||
af2ca7a67e | |||
9e3e2ff81a | |||
a9fe6472d9 | |||
a862a81480 | |||
dbb92881a1 | |||
10bf7f5d07 | |||
1e61d84fd1 | |||
8618ba1b60 | |||
3c8c44155d | |||
2002412026 | |||
7f69517fd4 | |||
851f8645b5 | |||
c40cfaa388 | |||
0349d1d57c | |||
53049c02ee | |||
73a3a61729 | |||
5fe6aa2800 | |||
c7eafd7c79 | |||
10b5fb5d72 | |||
c4eaa944e2 | |||
a735939d1e | |||
6ed5f04970 | |||
b459b09b2f | |||
3f5877dbcc | |||
e659b91c4d | |||
e09f054058 | |||
b646f50265 | |||
0a48ef3030 | |||
ba614801ee | |||
fd6eb47e68 | |||
e69aeb8b98 | |||
26ea1da146 | |||
c9e8c7a290 | |||
5e4eb92443 | |||
461b6499ef | |||
c769920b6e | |||
181b98ef9e | |||
4e1184a400 | |||
e52d9e3210 | |||
dc6475c91b | |||
52f9956e92 | |||
0bf00d1ca4 | |||
d1a707df57 | |||
4dc9b45297 | |||
6e31eebfb5 | |||
a7df828932 | |||
517cf61d11 | |||
4be7bc8323 | |||
74c05d00a9 | |||
677613d30a | |||
bacba629a5 | |||
14e36f1362 | |||
d43ad849d1 | |||
627aa61184 | |||
dad5b17ac8 | |||
fef52c0112 | |||
8c4765b386 | |||
7c121bfc01 | |||
942c5cc04b | |||
348b3036ff | |||
09d3451d9d | |||
b1a49e5f29 | |||
da01a5b4dc | |||
3f9cdd9b56 | |||
0f9e87d7bb | |||
0869789214 | |||
10c8cc35c5 | |||
30c2e3e8ff | |||
86cc2f1075 | |||
fa357a450b | |||
b32641db87 | |||
0ee790969d | |||
7844ace934 | |||
f4993d6e5d | |||
0fab806f36 | |||
be2113d291 | |||
625d5b2313 | |||
6471c0c536 | |||
47c53fa60a | |||
cf50e4f6ec | |||
7eea97d741 | |||
88b55ab93e | |||
ee36d47c27 | |||
6f2fdbe447 | |||
0f36be0001 | |||
0f4a197e34 | |||
7dbff5b9e6 | |||
220246278a | |||
349e5a15e9 | |||
bf7f4bba7b | |||
ab1766a559 | |||
51bf33040a | |||
a2c7273801 | |||
ec6ac5bf24 | |||
ec7501782d | |||
890b1c2d52 | |||
c25d07259a | |||
c960246eee | |||
a01aee3111 | |||
e2e951efdf | |||
3f6393f732 | |||
b6eb343234 | |||
207a7e5160 | |||
a0face4a28 | |||
a8cf9f5cc4 | |||
461b38e653 | |||
8e4c0f7c22 | |||
d78bfcc35c | |||
2b7c09e6ee | |||
036d9dbe59 | |||
1d342cc6af | |||
62b32b2211 | |||
ae45ce517e | |||
5b3ccab7dc | |||
95f16c38a9 | |||
d616cb283b | |||
9874fe2c23 | |||
520a142992 | |||
6ff56dc0bb | |||
1e63615592 | |||
3e62ffed0a | |||
b133d51a83 | |||
037b89f018 | |||
20d06d9f9d | |||
156cf7315c | |||
e2886e5303 | |||
c6cf330e70 | |||
6be3b62d78 | |||
c57af5e81b | |||
f7431f809e | |||
ea43c34de8 | |||
fb6e9fa58f | |||
b2ce1e8029 | |||
d90c51220f | |||
d1b14b68fa | |||
d911728611 | |||
86a7200012 | |||
6ddb7453e1 | |||
ad2355f8d3 | |||
582c498fe3 | |||
0a0c58d450 | |||
0dc592b819 | |||
f46300016d | |||
3e1a7c6102 | |||
f07065bf84 | |||
6d79903eb3 | |||
e166329f34 | |||
bb1bf6a88c | |||
30cbb6c9a8 | |||
4e33ab1e89 | |||
5494f309c0 | |||
3b6e7eccdd | |||
e41d6787bb | |||
ed30108961 | |||
12712ef812 | |||
0307f6b42c | |||
3e44620966 | |||
7424f1f768 | |||
b5331d821c | |||
27f6d47efa | |||
dbc7ad2ec4 | |||
7b27d270a2 | |||
97b3a0b093 | |||
06b38506d1 | |||
fd581ffc37 | |||
ff57c5e9d3 | |||
9b16d7c786 | |||
4c1bb18956 | |||
7d2bf892b1 | |||
a99e77093f | |||
92737bb695 | |||
9b81955544 | |||
4a0031080a | |||
40e9fba312 | |||
e227cc92ff | |||
73dbdbcbe6 | |||
3961f26635 | |||
e51c274a18 | |||
e75d0c58a9 | |||
9a798360f4 | |||
844ad09464 | |||
1e1d1efd90 | |||
240e6835c2 | |||
61398ee8f8 | |||
e6e84859b7 | |||
abcdd331db | |||
775d136b91 | |||
dc93691fd9 | |||
48d782c69c | |||
0a04e626d7 | |||
e7c4bf5ebf | |||
546a416f7e | |||
179a7a2792 | |||
251b6fcf70 | |||
ab1fffb721 | |||
da352a322c | |||
7d20458e82 | |||
5a54066f81 | |||
a58e5a3399 | |||
9872f43cbf | |||
1078cc4642 | |||
db7ae028b2 | |||
2b6f5dbd59 | |||
f7aa0c45df | |||
a72d58cdf9 | |||
067283834a | |||
cf362c4a61 | |||
496245c801 | |||
859ab36347 | |||
1d740c7c36 | |||
a03c4c3659 | |||
094ecceaac | |||
2812736ae5 | |||
6f87f8706c | |||
38beebe720 | |||
fc1c3c6808 | |||
96ba895b84 | |||
df35dfe3bf | |||
c5504c6657 | |||
530e109433 | |||
6cce47b2f1 | |||
6185d5eca1 | |||
685ad1746e | |||
891f870ec0 | |||
ad9933f0f6 | |||
1b86117754 | |||
eeb3c968d6 | |||
406658a10f | |||
6a0551cea1 | |||
553f3b45d2 | |||
064a8e785c | |||
21e9723bb2 | |||
60b2c44a44 | |||
c4fe3ecc0a | |||
2f18a8f6d0 | |||
5ac784e18a | |||
7a2164b4d0 | |||
0a43eae184 | |||
3117e2b2a3 | |||
41fece4643 | |||
7aa807ec7f | |||
4d16e1e14a | |||
73fc18099e | |||
e34dac8dbb | |||
af0e7f7187 | |||
a3a6812608 | |||
2725c4ad4d | |||
c8cd6e9460 | |||
0cd27d6129 | |||
b47fc1c020 | |||
de6ef7ef5e | |||
f95fe8f1da | |||
bd0dee5db5 | |||
c13b7fd883 | |||
f7e74b3088 | |||
343f01d5e1 | |||
08bacdd090 | |||
41b1c80492 | |||
e5d7316e5d | |||
b043c3a6da | |||
98b2855b9c | |||
f3c52c409f | |||
1307bdc612 | |||
8c2e6971fc | |||
1903990f30 | |||
7d67005709 | |||
9acc2f92d1 | |||
72dfb0bda3 | |||
1635612430 | |||
abda837d2f | |||
101fb0b8aa | |||
10de7dc1f9 | |||
d2309b8114 | |||
6bdd81623f | |||
f538c9f0c3 | |||
8ae3bad6f7 | |||
77de99b383 | |||
312949f336 | |||
1ab635bd7e | |||
b35abd548c | |||
30e1c3307c | |||
08e052380a | |||
0e824c35cc | |||
548374ac2d | |||
9ad79fefc9 | |||
49d37c016e | |||
7c70c79a84 | |||
6916b8bff7 | |||
73dfb5f443 | |||
69b9dfa468 | |||
ab61b8ba0a | |||
5c8c64242f | |||
ddf96943f0 | |||
e773be2f58 | |||
f965804e6d | |||
ec078eba72 | |||
1815cb1bc3 | |||
7b3cd71085 | |||
06128b5d07 | |||
990c8e1f18 | |||
a170506356 | |||
5ecf2a3357 | |||
fa48746ba9 | |||
e2b8c069d7 | |||
14407bd1aa | |||
08db72903c | |||
46f9fab140 | |||
b7d06f2c0a | |||
118196a0bf | |||
586ce1fc80 | |||
adb979df38 | |||
3401cb5b4a | |||
ebf1f12e97 | |||
5766208207 | |||
4bf4771f08 | |||
0e87db9eb7 | |||
1e053783f3 | |||
7afc96112b | |||
7bb41a30ed | |||
3d1b0eb843 | |||
5b9af0b5ae | |||
9219ec539d | |||
c8bd57cd4d | |||
53bf8c09fd | |||
651c383668 | |||
9ed7587e3e | |||
674295e800 | |||
6b02f2eea0 | |||
5237e55326 | |||
3b59592110 | |||
72640ae058 | |||
d916027e75 | |||
8966d2aa06 | |||
de7ea04f54 | |||
bf71fabe0e | |||
b3368acb33 | |||
87220c6697 | |||
a3b5c2ad71 | |||
fb4c1473c5 | |||
2bb2502d20 | |||
fe51698579 | |||
0f0f40bff3 | |||
a798fe72d3 | |||
fba98d03a5 | |||
7dd2517f67 | |||
641477d6f6 | |||
8e56656c8d | |||
564a6a1f62 | |||
69f0c80cd7 | |||
6fcb51cea2 | |||
c58b8a4973 | |||
c8f4ab4f06 | |||
e425c4cca8 | |||
056ec986c2 | |||
de3b2fb95b | |||
789e39c719 | |||
b29c3152db | |||
3831679772 | |||
596f9566d8 | |||
124befe9d6 | |||
895534f32b | |||
50c16fe6de | |||
b092d1a5d3 | |||
a9b45630d7 | |||
c1a39c269e | |||
6fa3f0b6cd | |||
9e5e3d1559 | |||
7135205299 | |||
d99d977d2b | |||
7dd7e562bc | |||
75d857027e | |||
17694c1d01 | |||
749ad700d8 | |||
8f3df3039a | |||
02c315c194 | |||
b697375573 | |||
c57ef7725e | |||
3ae07503f2 | |||
9a0341adde | |||
96225efd96 | |||
c3bd904f41 | |||
74257163b1 | |||
7bc75fd220 | |||
a23281afab | |||
9e90dbdde6 | |||
1e8d8e40a2 | |||
583e208c1e | |||
9b91c427a1 | |||
196c5e9c24 | |||
d8f7feb672 | |||
7c889f17b9 | |||
c15a75556d | |||
5275c2e35f | |||
5267e06969 | |||
b62183adcb | |||
5d2dec1803 | |||
4a98dab948 | |||
9f8c70b326 | |||
a65404e63a | |||
05a1ddad05 | |||
4be3d63c0e | |||
de6ce4a46e | |||
7a9e5afb93 | |||
b2876b0a03 | |||
b66f66fe6a | |||
30f04962d4 | |||
0feeb88024 | |||
b799f2e05b | |||
56d21de001 | |||
7b54aa0c7d | |||
6e11b8ada1 | |||
98d25fc4e9 | |||
79405f47f6 | |||
1c7c4fc3b0 | |||
97e84d0977 | |||
9906b58818 | |||
371f084884 | |||
1c10839c14 | |||
c55fdd9834 | |||
67b0890a6e | |||
4da4f96669 | |||
60a64c2558 | |||
d4153da8b9 | |||
fc33ab8905 | |||
a123c64f59 | |||
a090fe3834 | |||
8fa84de28e | |||
3e3da38de1 | |||
a3be8ccc87 | |||
cdfef30c22 | |||
cabf1e82e8 | |||
608dc5e284 | |||
836d40072f | |||
431401d90e | |||
6da83e2bd7 | |||
7bccfc0006 | |||
e051e09c1d | |||
1462c57d0c | |||
77c2b2b326 | |||
3cf9b9d9a5 | |||
629117e594 | |||
08f87c321f | |||
1ff13723fe | |||
510bda1b28 | |||
890327b381 | |||
b21f7411dd | |||
5df623bd2e | |||
1e9d249f71 | |||
a7b5949e6a | |||
02010170ce | |||
35998142fe | |||
75ea453bf4 | |||
33061aaa0d | |||
e342311bef | |||
3d743a1ba1 | |||
abca618008 | |||
0d2c3901a3 | |||
d901a1531f | |||
d8d56b195f | |||
a52699717c | |||
98315de723 | |||
68d2f7d4c0 | |||
c812b5ee09 | |||
dcf487bdc1 | |||
547b1d3720 | |||
84f75f4156 | |||
ff69da3fa2 | |||
edffe92a24 | |||
b6fe40ada4 | |||
837804b231 | |||
81deed143b | |||
900cdd3fa1 | |||
0018dc6ce7 | |||
c92f914081 | |||
0498444ef2 | |||
ce3c34e458 | |||
20401b99d8 | |||
397f98513b | |||
e545ea9504 | |||
b867d8f731 | |||
9a68864b67 | |||
72d7178762 | |||
fbcd9a0c1d | |||
c3144a20db | |||
5b56e0462d | |||
b7fffbb6df | |||
1f346230e3 | |||
a2860a7c8c | |||
df997e5d3b | |||
a67a82c921 | |||
ea0fe8d3d2 | |||
2560042ac7 | |||
3d1d0696b9 | |||
83f893f50b | |||
9ecf95b075 | |||
7748c261da | |||
a2db44f80c | |||
b438d8aec0 | |||
4ac169b210 | |||
56dc6d7f1e | |||
45b8762188 | |||
cafab98d10 | |||
9256f910f0 | |||
32068a832a | |||
47c2c0376a | |||
f0dadc4a43 | |||
960b60cd2d | |||
d6abd72e55 | |||
0a568f2530 | |||
c52aa648c0 | |||
3d23b39f4c | |||
f3a4048ebf | |||
1b07637cc4 | |||
68b75fd558 | |||
7c5ec1853d | |||
e8f4686430 | |||
02348924d0 | |||
69dcb4dbda | |||
c838821615 | |||
8b4ac7801f | |||
64a411628d | |||
e8e25c6fd6 | |||
62485b6851 | |||
54025d2bf5 | |||
f5ebf79e71 | |||
66d5490702 | |||
42fe052f9f | |||
58d9c46a9b | |||
e4648e2138 | |||
110e047681 | |||
17d403d812 | |||
0a53bd4956 | |||
e52d05c7db | |||
b00db4f8a2 | |||
0c2f30fd45 | |||
e08871c637 | |||
ff715881bc | |||
0e2e5ffa52 | |||
8095c4c155 | |||
e86246a985 | |||
625aaa02eb | |||
787e35c9f3 | |||
8887e6af91 | |||
dde4c751da | |||
3c39baf1d6 | |||
b292124f3c | |||
c0035ba1a2 | |||
2491509c6a | |||
107935ed31 | |||
31491c62c5 | |||
eacf8b896a | |||
7936fc5bd8 | |||
adfaddbcf4 | |||
74db5c6be7 | |||
f9399bcce7 | |||
87600b23db | |||
cedfb17b18 | |||
fa4c83df6b | |||
42c8720e8b | |||
b334d89715 | |||
4f5d36a84d | |||
8f379e2262 | |||
fa11a6e18b | |||
52bedce8f4 | |||
4c82af36e6 | |||
dafa0d9138 | |||
2e0450d7ed | |||
6af3209d4d | |||
5d362047e2 | |||
f48d6ca9f8 | |||
964e8e0a17 | |||
1f60a2d8b9 | |||
5fd83f2757 | |||
c80df4140b | |||
53e1729e2f | |||
ab2d1122a9 | |||
5190594c8a | |||
c858ceeb58 | |||
f0f52b9166 | |||
00c6f74481 | |||
2177ba0ed2 | |||
3483515346 | |||
75a06d2a40 | |||
53ac11983b | |||
69f4a4d4f8 | |||
222bcb808f | |||
686483f51a | |||
8df3da11e3 | |||
84dafda0e4 | |||
b909facfe5 | |||
7780d94de1 | |||
f2c440e466 | |||
4937e004b5 | |||
4cb383dccb | |||
c8a4b6f23c | |||
857724c7e6 | |||
a9b0400d13 | |||
2d1e5bbc7e | |||
60627ce756 | |||
7961a09d16 | |||
613efcacc7 | |||
7e8db16e18 | |||
1fbbed7e23 | |||
984272beb4 | |||
b9ce94bb68 | |||
f4c4ee78d9 | |||
793596614e | |||
136280100c | |||
29f1e4d2c9 | |||
72a7e61fd0 | |||
381cfca67f | |||
f40620aa25 | |||
57a9fed42b | |||
18d820da94 | |||
26e66f046f | |||
4270c04856 | |||
74456d1135 | |||
62dc824bc0 | |||
1605791f1b | |||
37a46aa2cf | |||
1d2d217b94 | |||
23961f695d | |||
730b208617 | |||
f09c04eeac | |||
be73739c62 | |||
eea3fb48a8 | |||
b4fa72c058 | |||
b0a865b0f1 | |||
7f49731618 | |||
3410aea788 | |||
bc0a133bb1 | |||
7e287a5359 | |||
1110bd0851 | |||
1b576f826d | |||
fe17566370 | |||
e3c00669c1 | |||
33d17afc32 | |||
2388359a99 | |||
2df0c9503c | |||
61fa3bc77c | |||
03ac9b6956 | |||
dfbef8495d | |||
7b17c49d8f | |||
4b3f31c2ee | |||
9ccc65bf8f | |||
f9e22add03 | |||
846951cda7 | |||
97836e18b2 | |||
7b69df4db2 | |||
3767b4bbe7 | |||
d7d2eefa4f | |||
6737f28d1e | |||
3da9404c2d | |||
4d5bd0fa32 | |||
1137da37c3 | |||
495a18805c | |||
a226b82d0b | |||
0b5ddcdc9b | |||
82da8f4946 | |||
5ff481ce3c | |||
f21dcaa6fb | |||
2c940de598 | |||
ce75b776bb | |||
7d22b9b9f9 | |||
6cb8b3b5cd | |||
2bf4017f2b | |||
08d2f8568b | |||
ac5f45d2d4 | |||
3cc7ad7d20 | |||
d4513364fb | |||
9684f4e42a | |||
f4186981fd | |||
141689e697 | |||
743c8b44a2 | |||
5e1459564a | |||
69a8813a3d | |||
17175df835 | |||
6b32535cb6 | |||
7f8fe75ab2 | |||
2815a14bb5 | |||
f4dfa60790 | |||
35e88dd529 | |||
4d5094a517 | |||
dd5abae721 | |||
8f2fb20934 | |||
44143f481a | |||
440abf4998 | |||
3c10427e04 | |||
74555a32ed | |||
85956b5828 | |||
41e40cad03 | |||
df2d5c6585 | |||
1a111b706e | |||
f696fce187 | |||
82d3d81bb2 | |||
4668932bac | |||
e6c41eac93 | |||
f0cff661df | |||
82d20dea39 | |||
804bb06859 | |||
5afa7e53f8 | |||
7f15b7b716 | |||
552e0c2248 | |||
e5b9e1f5e7 | |||
502bf90007 | |||
40bf117497 | |||
4011dce31b | |||
9e881e32f8 | |||
14aad2358f | |||
637a8899c5 | |||
cf0e395921 | |||
6ef438ce50 | |||
46e4b977a4 | |||
9626c5dead | |||
aea364e43d | |||
06defd0cb0 | |||
0f80897c50 | |||
57bb1c2c0d | |||
7b35b414e8 | |||
761aac7a23 | |||
15a02d7664 | |||
16ed68c1ec | |||
e63cf660c6 | |||
aaff484306 | |||
3281d9a215 | |||
0fcd61e00f | |||
c4523ea470 | |||
0447b3e4cc | |||
4d27c2901b | |||
855e18b31c | |||
d790878af6 | |||
85b244df2f | |||
6070afa6b6 | |||
975594703d | |||
6b8c3ef614 | |||
9b22f05381 | |||
ca3a990f9e | |||
557f4f689f | |||
66574d058a | |||
1c7c67060d | |||
9827ee97ad | |||
71a9a84211 | |||
367a2a4cee | |||
4f7465ba44 | |||
f891fc698c | |||
36bec62c9a | |||
dd5a2c8315 | |||
56bff46481 | |||
b83a0adb19 | |||
92ffefe656 | |||
51b2e41879 | |||
ef43bc9208 | |||
33733a4001 | |||
e5a1b37981 | |||
30aa72dc8e | |||
2c2d474059 | |||
c55ac0450f | |||
2d26b9c994 | |||
f38fe092ee | |||
7a33eb163b | |||
5db0408b9f | |||
3557d38ce0 | |||
7de4e9e66a | |||
73838ccb8b | |||
0509de76d5 | |||
f4b3d19059 | |||
f37fb82d53 | |||
dbe98f3fa5 | |||
371d4768e6 | |||
562d8386ec | |||
1625e4eb85 | |||
2365a076ac | |||
9898791771 | |||
a1c658274d | |||
be9998b48b | |||
e8f308f654 | |||
07132a2c42 | |||
9c4582e283 | |||
0204002d9b | |||
b3107cfad0 | |||
91ae68c07e | |||
06b3bf27b5 | |||
fbef63e150 | |||
bb8ee9bb3e | |||
25677a4126 | |||
3aeca0a770 | |||
4bd4733e52 | |||
9acec4d952 | |||
8388adcd1d | |||
5988ba76b5 | |||
1a06e7a16e | |||
7241cef7a5 | |||
5145296486 | |||
2cbf2d2226 | |||
754664aefa | |||
af99173cd7 | |||
fd1f30f92b | |||
d9ab2f8b90 | |||
bd6c60cf8a | |||
f0c150d93b | |||
c2986eaf47 | |||
ef0c4797bb | |||
ac02a99934 | |||
fb67d1155f | |||
eb46852bb9 | |||
007d8d2811 | |||
ebe04fc114 | |||
d7dd7f70c0 | |||
f2cb89a128 | |||
b8fade23de | |||
3b97a17648 | |||
0d06e3ff22 | |||
c914f7bbcf | |||
1b451180c1 | |||
ed061b362b | |||
e1026584c8 | |||
4c615e4fac | |||
7c9d48833b | |||
b60b195aec | |||
db76c8d7f4 | |||
de92740e87 | |||
522bf91c30 | |||
48d3abc1fe | |||
3f6f25e06f | |||
34ba07ee3b | |||
ac37319d20 | |||
b2c6274f74 | |||
402884b5ce | |||
23c99002c0 | |||
ee115b3337 | |||
82f5a141ed | |||
0567168ea9 | |||
c80a15846d | |||
5e194536a8 | |||
43c5ab8ecc | |||
cd295228ef | |||
6c42221620 | |||
0d73a7cd07 | |||
39d5b7edb0 | |||
6fa50a699f | |||
ddaef3e5d5 | |||
c3e9d4a9f8 | |||
7530fb67c8 | |||
19bb56df47 | |||
b0073ac933 | |||
137a89da15 | |||
44da7a302f | |||
4096aae8d4 | |||
d3e026d82a | |||
fa5ecd6495 | |||
af209ad50e | |||
7b89228fa7 | |||
d31a88206c | |||
cd4ed8765b | |||
b6f780d70d | |||
b071a58ca7 | |||
ce554f7718 | |||
99b1cec2e1 | |||
46911a8905 | |||
4eb61529f6 | |||
81abf29bec | |||
85897ef8cd | |||
b824c0b125 | |||
6367c6d116 | |||
a7736d88a9 | |||
049dbf5a78 | |||
4ac92caeb5 | |||
7af3da2a97 | |||
95a62fcdd1 | |||
7872d20554 | |||
a598eb7e98 | |||
c786acc39b | |||
07d8052a57 | |||
db9edb477e | |||
9bd3a6758a | |||
2cb1560bbd | |||
006059438f | |||
84ea3b9788 | |||
b667abde10 | |||
aa10997171 | |||
7880ac1909 | |||
f53848b4b9 | |||
82f2f38680 | |||
dcc2549574 | |||
cfe4753913 | |||
fcb1a7e4d4 | |||
ce76a7dfa5 | |||
7c1de81861 | |||
eddad20acc | |||
7daad57862 | |||
1468049fe9 | |||
3b91e59a79 | |||
3496a30528 | |||
32bad5df15 | |||
3f58eca1be | |||
2350843d1d | |||
a2588a178c | |||
e5292696c4 | |||
34b2a65ccb | |||
3aa3659bc7 | |||
b8117394c0 | |||
fd2bbd2b59 | |||
127c470746 | |||
f2844bdf1a | |||
c5bfef4264 | |||
73863acc12 | |||
19e99204b9 | |||
13f5b94c3e | |||
3a2498401d | |||
53b20ba625 | |||
e7f6f0950f | |||
9fbe1b38a5 | |||
078485134d | |||
67b1766e32 | |||
d4b69ac79c | |||
e61a2d7083 | |||
c03f6604af | |||
572bb38ddb | |||
42c5c0cb9f | |||
e145d2255e | |||
442fa07dd4 | |||
31ae9e1243 | |||
d7f83f8df2 | |||
29e2d4e0c8 | |||
2732d2c844 | |||
c4a037b277 | |||
0e614ad6fc | |||
ca1a8cd617 | |||
ba96a637be | |||
c2cac772e3 | |||
6b7216f4ec | |||
e4fb5946dd | |||
ca61248861 | |||
68d7b4649e | |||
0416aacbbd | |||
bc731e6f8e | |||
ae5d7705bb | |||
b9bd541532 | |||
83639c2535 | |||
25d80f4df1 | |||
74f918d911 | |||
a20efa56eb | |||
f4d83075be | |||
254592c383 | |||
ee23ac0537 | |||
a48cf0bb24 | |||
dae59238cd | |||
8736da1a21 | |||
09a1de69e7 | |||
63d67bc6cb | |||
7099245204 | |||
4d097d2139 | |||
6485bf9ad9 | |||
b7c5b1bfc7 | |||
2b7546e827 | |||
3549ccf4b3 | |||
e2f5752d9a | |||
1a59019fc8 | |||
7bac7bdc3e | |||
19fe58dbac | |||
0a5b30e21c | |||
664818fd29 | |||
d5214e2505 | |||
d906fcea0e | |||
29c8e8b740 | |||
71fec4c555 | |||
5ee36c897d | |||
4aba0c7405 | |||
ed7479c854 | |||
8d3d5f726a | |||
a9a7068818 | |||
1bde7c7718 | |||
17068130bb | |||
81a91d62cb | |||
2575263438 | |||
7f0e25cb50 | |||
a1e4e9c50f | |||
98eff2701b | |||
8b84f87217 | |||
306a1b7bc2 | |||
481214c46e | |||
a5961cbeab | |||
3bf335e0a0 | |||
68f696d165 | |||
1170aed026 | |||
bf1b2066b6 | |||
4c080afb76 | |||
ee1c43ca91 | |||
1c2e6f9e4c | |||
dd379430d9 | |||
42033ebd35 | |||
a086d6e009 | |||
c70bbdab26 | |||
3d956ef554 | |||
329f491c30 | |||
e93701f50e | |||
e680de05ea | |||
56fec674c5 | |||
54d92a027a | |||
319ac3a641 | |||
0a03c46351 | |||
ae1b62e147 | |||
8d567f6b06 | |||
b1ef09675b | |||
2b7b925090 | |||
e0454e95db | |||
91e421d961 | |||
c853afe769 | |||
1a64cb38d5 | |||
ccebd22856 | |||
a1f3b82333 | |||
3dda29781e | |||
a9d297ee31 | |||
e5ff61f201 | |||
d116eb7655 | |||
bc726c6334 | |||
123473dfc8 | |||
d9eccd4fba | |||
5b890847e5 | |||
64c85b9617 | |||
3e3b0bcd8b | |||
4c1eb1b12a | |||
530d03d284 | |||
619fa9b65e | |||
0032235933 | |||
61d1f1ea87 | |||
238d27acdc | |||
2f62271453 | |||
75d5117a2d | |||
b4700af2f5 | |||
374e2b311d | |||
49036abbaf | |||
38ccbac97c | |||
6b4896b8f5 | |||
d582d1cc42 | |||
9e2b8a2aa9 | |||
6fdc733941 | |||
422b390c48 | |||
67a9d1285c | |||
8e26e38ecc | |||
02e12d8575 | |||
fe2954ce08 | |||
1fe4439395 | |||
2ff04d2abd | |||
3f30d3aa89 | |||
129e17b33a | |||
bf2d8c3f4b | |||
b29f04ce01 | |||
d185ebad48 | |||
605df7c91c | |||
ec60cad8bb | |||
6aa0f5a392 |
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: irmen
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
36
.github/workflows/all-ci.yml
vendored
Normal file
36
.github/workflows/all-ci.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: Build and Test the Prog8 compiler
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- 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@v3
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
|
||||
- name: Build and test with Gradle
|
||||
run: ./gradlew build shadowJar --no-daemon
|
||||
|
||||
- name: Create compiler shadowJar artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: prog8-compiler-jar-zipped
|
||||
path: compiler/build/libs/*-all.jar
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -15,6 +15,7 @@ out/
|
||||
parser/**/*.interp
|
||||
parser/**/*.tokens
|
||||
parser/**/*.java
|
||||
compiler/src/prog8/buildversion/*
|
||||
*.py[cod]
|
||||
*.egg
|
||||
*.egg-info
|
||||
@ -26,8 +27,11 @@ parser.out
|
||||
parsetab.py
|
||||
.pytest_cache/
|
||||
.attach_pid*
|
||||
compiler/lib/
|
||||
|
||||
.gradle
|
||||
**/BuildVersion.kt
|
||||
/prog8compiler.jar
|
||||
sd*.img
|
||||
*.d64
|
||||
|
||||
|
8
.idea/codeInsightSettings.xml
generated
Normal file
8
.idea/codeInsightSettings.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaProjectCodeInsightSettings">
|
||||
<excluded-names>
|
||||
<name>kotlin.Result</name>
|
||||
</excluded-names>
|
||||
</component>
|
||||
</project>
|
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
7
.idea/compiler.xml
generated
Normal file
7
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="3000" />
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
@ -7,6 +7,7 @@
|
||||
<language isEnabled="false" name="Groovy" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="processCode" value="false" />
|
||||
|
3
.idea/kotlinc.xml
generated
3
.idea/kotlinc.xml
generated
@ -3,4 +3,7 @@
|
||||
<component name="Kotlin2JvmCompilerArguments">
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.20" />
|
||||
</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:1.9.22" />
|
||||
<CLASSES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/antlr_4_9_complete.xml
generated
9
.idea/libraries/antlr_4_9_complete.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr-4.9-complete">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-4.9-complete.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
18
.idea/libraries/antlr_antlr4.xml
generated
Normal file
18
.idea/libraries/antlr_antlr4.xml
generated
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr.antlr4" type="repository">
|
||||
<properties maven-id="org.antlr:antlr4:4.13.1">
|
||||
<exclude>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.1/antlr4-4.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.1/antlr4-runtime-4.13.1.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.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!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/antlr_runtime_4_9.xml
generated
9
.idea/libraries/antlr_runtime_4_9.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr-runtime-4.9">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-runtime-4.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/dbus_java_3_2_4.xml
generated
9
.idea/libraries/dbus_java_3_2_4.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="dbus-java-3.2.4">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/dbusCompilerService/lib/dbus-java-3.2.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
@ -0,0 +1,25 @@
|
||||
<component name="libraryTable">
|
||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/glassfish_javax_json.xml
generated
Normal file
10
.idea/libraries/glassfish_javax_json.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<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>
|
23
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
23
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
@ -0,0 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.8.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.8.0/kotest-assertions-core-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.8.0/kotest-assertions-shared-jvm-5.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/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.8.0/kotest-common-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.8.0/kotest-assertions-api-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
44
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
44
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
@ -0,0 +1,44 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.8.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.8.0/kotest-runner-junit5-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.8.0/kotest-framework-api-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.8.0/kotest-assertions-shared-jvm-5.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/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.8.0/kotest-common-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.8.0/kotest-framework-engine-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.162/classgraph-4.8.162.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.8.0/kotest-framework-discovery-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.8.0/kotest-assertions-core-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.8.0/kotest-assertions-api-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.8.0/kotest-extensions-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.8.0/kotest-framework-concurrency-jvm-5.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/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-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/javax_json_api_1_1_4.xml
generated
10
.idea/libraries/javax_json_api_1_1_4.xml
generated
@ -1,10 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="javax.json-api-1.1.4">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/javax.json-api-1.1.4.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/javax.json-1.1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<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.6" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/kotlinx_cli_jvm.xml
generated
9
.idea/libraries/kotlinx_cli_jvm.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-cli-jvm">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/compiler/lib/kotlinx-cli-jvm-0.3.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
@ -0,0 +1,15 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/slf4j_api_1_7_30.xml
generated
10
.idea/libraries/slf4j_api_1_7_30.xml
generated
@ -1,10 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="slf4j-api-1.7.30">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/slf4j-api-1.7.30.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/slf4j-simple-1.7.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
@ -0,0 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="slf4j.simple" type="repository">
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.11" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.11/slf4j-simple-2.0.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
18
.idea/libraries/takes.xml
generated
Normal file
18
.idea/libraries/takes.xml
generated
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="takes" type="repository">
|
||||
<properties maven-id="org.takes:takes:1.24.4" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
12
.idea/libraries/takes_http.xml
generated
12
.idea/libraries/takes_http.xml
generated
@ -1,12 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="takes-http">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/cactoos-0.42.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/commons-lang3-3.7.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/commons-text-1.4.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/takes-1.19.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/unittest_libs.xml
generated
10
.idea/libraries/unittest_libs.xml
generated
@ -1,10 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="unittest-libs">
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/compiler/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/compiler/lib" recursive="false" />
|
||||
</library>
|
||||
</component>
|
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@ -4,7 +4,7 @@
|
||||
<option name="perGrammarGenerationSettings">
|
||||
<list>
|
||||
<PerGrammarGenerationSettings>
|
||||
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/prog8.g4" />
|
||||
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
|
||||
<option name="autoGen" value="true" />
|
||||
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
||||
<option name="libDir" value="" />
|
||||
@ -16,7 +16,13 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -2,13 +2,21 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" filepath="$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -3,4 +3,4 @@
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
25
.readthedocs.yaml
Normal file
25
.readthedocs.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
11
.travis.yml
11
.travis.yml
@ -1,11 +0,0 @@
|
||||
language: java
|
||||
sudo: false
|
||||
# jdk: openjdk8
|
||||
# dist: xenial
|
||||
|
||||
before_install:
|
||||
- chmod +x ./gradlew
|
||||
|
||||
script:
|
||||
- ./gradlew test
|
||||
|
10
LICENSE
10
LICENSE
@ -1,3 +1,13 @@
|
||||
|
||||
This sofware license is for Prog8 the compiler + associated library files.
|
||||
|
||||
Exception: All output files generated by the compiler (intermediary files
|
||||
and compiled binary programs) are excluded from this; you can do with those
|
||||
whatever you want.
|
||||
|
||||
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
68
README.md
68
README.md
@ -1,5 +1,4 @@
|
||||
[](https://saythanks.io/to/irmen)
|
||||
[](https://travis-ci.org/irmen/prog8)
|
||||
[](https://ko-fi.com/H2H6S0FFF)
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
@ -7,41 +6,68 @@ Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
|
||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||
|
||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
||||
|
||||
|
||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
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).
|
||||
|
||||
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:
|
||||
|
||||
- The compiler and its libraries are free to use according to the terms of the GNU GPL 3.0
|
||||
- *exception:* the resulting files (intermediate source codes and resulting binary program) created by the compiler
|
||||
are excluded from the GPL and are free to use in whatever way desired, commercially or not.
|
||||
|
||||
|
||||
What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- 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
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- automatic variable allocations, automatic string and array variables and string sharing
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
- 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
|
||||
- sane variable initialization, programs can be restarted again just fine after exiting to basic
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches
|
||||
- floating point operations (requires the C64 Basic ROM routines for this)
|
||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||
- structs to group together sets of variables and manipulate them at once
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``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``
|
||||
- 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
|
||||
- fast execution speed due to compilation to native assembly code
|
||||
- variables are allocated statically
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
|
||||
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
@ -51,11 +77,14 @@ What does Prog8 provide?
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
||||
- "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)
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
- "pet32": Commodore PET (experimental)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
|
||||
@ -72,7 +101,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
|
||||
|
10
build.gradle
Normal file
10
build.gradle
Normal file
@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
43
codeCore/build.gradle
Normal file
43
codeCore/build.gradle
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
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.18"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
14
codeCore/codeCore.iml
Normal file
14
codeCore/codeCore.iml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
24
codeCore/src/prog8/code/Either.kt
Normal file
24
codeCore/src/prog8/code/Either.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package prog8.code
|
||||
|
||||
/**
|
||||
* By convention, the right side of an `Either` is used to hold successful values.
|
||||
*/
|
||||
sealed class Either<out L, out R> {
|
||||
|
||||
data class Left<out L>(val value: L) : Either<L, Nothing>()
|
||||
|
||||
data class Right<out R>(val value: R) : Either<Nothing, R>()
|
||||
|
||||
fun isRight() = this is Right<R>
|
||||
|
||||
fun isLeft() = this is Left<L>
|
||||
|
||||
inline fun <C> fold(ifLeft: (L) -> C, ifRight: (R) -> C): C = when (this) {
|
||||
is Right -> ifRight(value)
|
||||
is Left -> ifLeft(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <L> left(a: L) = Either.Left(a)
|
||||
fun <R> right(b: R) = Either.Right(b)
|
256
codeCore/src/prog8/code/SymbolTable.kt
Normal file
256
codeCore/src/prog8/code/SymbolTable.kt
Normal file
@ -0,0 +1,256 @@
|
||||
package prog8.code
|
||||
|
||||
import prog8.code.ast.PtNode
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
/**
|
||||
* Tree structure containing all symbol definitions in the program
|
||||
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||
*/
|
||||
class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) {
|
||||
/**
|
||||
* The table as a flat mapping of scoped names to the StNode.
|
||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||
*/
|
||||
|
||||
private var cachedFlat: Map<String, StNode>? = null
|
||||
|
||||
val flat: Map<String, StNode> get() {
|
||||
if(cachedFlat!=null)
|
||||
return cachedFlat!!
|
||||
|
||||
val result = mutableMapOf<String, StNode>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
result[child.value.scopedName] = child.value
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
cachedFlat = result
|
||||
return result
|
||||
}
|
||||
|
||||
fun resetCachedFlat() {
|
||||
cachedFlat = null
|
||||
}
|
||||
|
||||
val allVariables: Collection<StStaticVariable> by lazy {
|
||||
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 {
|
||||
val vars = mutableListOf<StMemorySlab>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type== StNodeType.MEMORYSLAB)
|
||||
vars.add(child.value as StMemorySlab)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
vars
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: String) = flat[scopedName]
|
||||
|
||||
fun getLength(name: String): Int? {
|
||||
val node = flat[name]
|
||||
return when(node) {
|
||||
is StMemVar -> node.length
|
||||
is StMemorySlab -> node.size.toInt()
|
||||
is StStaticVariable -> node.length
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class StNodeType {
|
||||
GLOBAL,
|
||||
// MODULE, // not used with current scoping rules
|
||||
BLOCK,
|
||||
SUBROUTINE,
|
||||
ROMSUB,
|
||||
LABEL,
|
||||
STATICVAR,
|
||||
MEMVAR,
|
||||
CONSTANT,
|
||||
BUILTINFUNC,
|
||||
MEMORYSLAB
|
||||
}
|
||||
|
||||
|
||||
open class StNode(val name: String,
|
||||
val type: StNodeType,
|
||||
val astNode: PtNode,
|
||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
lateinit var parent: StNode
|
||||
|
||||
val scopedName: String by lazy { scopedNameList.joinToString(".") }
|
||||
|
||||
open fun lookup(scopedName: String) =
|
||||
lookup(scopedName.split('.'))
|
||||
|
||||
fun lookupUnscopedOrElse(name: String, default: () -> StNode) =
|
||||
lookupUnscoped(name) ?: default()
|
||||
|
||||
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
|
||||
lookup(scopedName.split('.')) ?: default()
|
||||
|
||||
fun lookupUnscoped(name: String): StNode? {
|
||||
// first consider the builtin functions
|
||||
var globalscope = this
|
||||
while(globalscope.type!= StNodeType.GLOBAL)
|
||||
globalscope = globalscope.parent
|
||||
val globalNode = globalscope.children[name]
|
||||
if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC)
|
||||
return globalNode
|
||||
|
||||
// search for the unqualified name in the current scope or its parent scopes
|
||||
var scope=this
|
||||
while(true) {
|
||||
val node = scope.children[name]
|
||||
if(node!=null)
|
||||
return node
|
||||
if(scope.type== StNodeType.GLOBAL)
|
||||
return null
|
||||
else
|
||||
scope = scope.parent
|
||||
}
|
||||
}
|
||||
|
||||
fun add(child: StNode) {
|
||||
children[child.name] = child
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
private val scopedNameList: List<String> by lazy {
|
||||
if(type==StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedNameList + name
|
||||
}
|
||||
|
||||
private fun lookup(scopedName: List<String>): StNode? {
|
||||
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||
var node = this
|
||||
while(node.type!=StNodeType.GLOBAL)
|
||||
node = node.parent
|
||||
|
||||
for(name in scopedName) {
|
||||
if(name in node.children)
|
||||
node = node.children.getValue(name)
|
||||
else
|
||||
return null
|
||||
}
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
class StStaticVariable(name: String,
|
||||
val dt: DataType,
|
||||
val 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, // used in the variable allocator
|
||||
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||
|
||||
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
|
||||
|
||||
init {
|
||||
if(length!=null) {
|
||||
require(onetimeInitializationNumericValue == null)
|
||||
if(onetimeInitializationArrayValue!=null)
|
||||
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||
}
|
||||
if(onetimeInitializationNumericValue!=null) {
|
||||
require(dt in NumericDatatypes)
|
||||
}
|
||||
if(onetimeInitializationArrayValue!=null) {
|
||||
require(dt in ArrayDatatypes)
|
||||
require(length==onetimeInitializationArrayValue.size)
|
||||
}
|
||||
if(onetimeInitializationStringValue!=null) {
|
||||
require(dt == DataType.STR)
|
||||
require(length == onetimeInitializationStringValue.first.length+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
|
||||
StNode(name, StNodeType.CONSTANT, astNode)
|
||||
|
||||
|
||||
class StMemVar(name: String,
|
||||
val dt: DataType,
|
||||
val address: UInt,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
astNode: PtNode) :
|
||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||
|
||||
init{
|
||||
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||
require(length!=null) { "memory mapped array or string must have known length" }
|
||||
}
|
||||
}
|
||||
|
||||
class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
val align: UInt,
|
||||
astNode: PtNode
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||
|
||||
|
||||
class StRomSub(name: String,
|
||||
val address: UInt?, // null in case of asmsub, specified in case of romsub
|
||||
val parameters: List<StRomSubParameter>,
|
||||
val returns: List<StRomSubParameter>,
|
||||
astNode: PtNode) :
|
||||
StNode(name, StNodeType.ROMSUB, astNode)
|
||||
|
||||
|
||||
|
||||
class StSubroutineParameter(val name: String, val type: DataType)
|
||||
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
206
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
206
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
@ -0,0 +1,206 @@
|
||||
package prog8.code
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import java.util.*
|
||||
|
||||
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
|
||||
fun make(): SymbolTable {
|
||||
val st = SymbolTable(program)
|
||||
|
||||
BuiltinFunctions.forEach {
|
||||
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
|
||||
}
|
||||
|
||||
val scopestack = Stack<StNode>()
|
||||
scopestack.push(st)
|
||||
program.children.forEach {
|
||||
addToSt(it, scopestack)
|
||||
}
|
||||
require(scopestack.size==1)
|
||||
|
||||
if(options.compTarget.name != VMTarget.NAME) {
|
||||
listOf(
|
||||
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||
).forEach {
|
||||
it.parent = program
|
||||
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||
}
|
||||
}
|
||||
|
||||
return st
|
||||
}
|
||||
|
||||
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||
val stNode = when(node) {
|
||||
is PtAsmSub -> {
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||
StRomSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
is PtBlock -> {
|
||||
StNode(node.name, StNodeType.BLOCK, node)
|
||||
}
|
||||
is PtConstant -> {
|
||||
StConstant(node.name, node.type, node.value, node)
|
||||
}
|
||||
is PtLabel -> {
|
||||
StNode(node.name, StNodeType.LABEL, node)
|
||||
}
|
||||
is PtMemMapped -> {
|
||||
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
|
||||
StSub(node.name, params, node.returntype, node)
|
||||
}
|
||||
is PtVariable -> {
|
||||
val initialNumeric: Double?
|
||||
val initialString: StString?
|
||||
val initialArray: StArray?
|
||||
val numElements: Int?
|
||||
val value = node.value
|
||||
if(value!=null) {
|
||||
val number = (value as? PtNumber)?.number
|
||||
initialNumeric = if(number==0.0) null else number // 0 as init value -> just uninitialized
|
||||
when (value) {
|
||||
is PtString -> {
|
||||
initialString = StString(value.value, value.encoding)
|
||||
initialArray = null
|
||||
numElements = value.value.length + 1 // include the terminating 0-byte
|
||||
}
|
||||
is PtArray -> {
|
||||
initialArray = makeInitialArray(value)
|
||||
initialString = null
|
||||
numElements = initialArray.size
|
||||
require(node.arraySize?.toInt()==numElements)
|
||||
}
|
||||
else -> {
|
||||
initialString = null
|
||||
initialArray = null
|
||||
numElements = node.arraySize?.toInt()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initialNumeric = null
|
||||
initialArray = null
|
||||
initialString = null
|
||||
numElements = node.arraySize?.toInt()
|
||||
}
|
||||
// if(node.type in SplitWordArrayTypes) {
|
||||
// ... split array also add _lsb and _msb to symboltable?
|
||||
// }
|
||||
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if(node.name=="memory") {
|
||||
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||
val slabname = (node.args[0] as PtString).value
|
||||
val size = (node.args[1] as PtNumber).number.toUInt()
|
||||
val align = (node.args[2] as PtNumber).number.toUInt()
|
||||
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
}
|
||||
null
|
||||
}
|
||||
else -> null // node is not present in the ST
|
||||
}
|
||||
|
||||
if(stNode!=null) {
|
||||
scope.peek().add(stNode)
|
||||
scope.push(stNode)
|
||||
}
|
||||
node.children.forEach {
|
||||
addToSt(it, scope)
|
||||
}
|
||||
if(stNode!=null)
|
||||
scope.pop()
|
||||
}
|
||||
|
||||
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||
return value.children.map {
|
||||
when(it) {
|
||||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name)
|
||||
}
|
||||
is PtIdentifier -> StArrayElement(null, it.name)
|
||||
is PtNumber -> StArrayElement(it.number, null)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// override fun visit(decl: VarDecl) {
|
||||
// val node =
|
||||
// when(decl.type) {
|
||||
// VarDeclType.VAR -> {
|
||||
// var initialNumeric = (decl.value as? NumericLiteral)?.number
|
||||
// if(initialNumeric==0.0)
|
||||
// initialNumeric=null // variable will go into BSS and this will be set to 0
|
||||
// val initialStringLit = decl.value as? StringLiteral
|
||||
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
|
||||
// val initialArrayLit = decl.value as? ArrayLiteral
|
||||
// val initialArray = makeInitialArray(initialArrayLit)
|
||||
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
|
||||
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
|
||||
// val numElements =
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else if(initialStringLit!=null)
|
||||
// initialStringLit.value.length+1 // include the terminating 0-byte
|
||||
// else
|
||||
// null
|
||||
// val bss = if(decl.datatype==DataType.STR)
|
||||
// false
|
||||
// else if(decl.isArray)
|
||||
// initialArray.isNullOrEmpty()
|
||||
// else
|
||||
// initialNumeric == null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
|
||||
// }
|
||||
// VarDeclType.CONST -> {
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
|
||||
// }
|
||||
// VarDeclType.MEMORY -> {
|
||||
// val numElements =
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
|
||||
// }
|
||||
// }
|
||||
// scopestack.peek().add(node)
|
||||
// // st.origAstLinks[decl] = node
|
||||
// }
|
||||
//
|
||||
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
|
||||
// if(arrayLit==null)
|
||||
// return null
|
||||
// return arrayLit.value.map {
|
||||
// when(it){
|
||||
// is AddressOf -> {
|
||||
// val scopedName = it.identifier.targetNameAndType(program).first
|
||||
// StArrayElement(null, scopedName)
|
||||
// }
|
||||
// is IdentifierReference -> {
|
||||
// val scopedName = it.targetNameAndType(program).first
|
||||
// StArrayElement(null, scopedName)
|
||||
// }
|
||||
// is NumericLiteral -> StArrayElement(it.number, null)
|
||||
// else -> throw FatalAstException("weird element dt in array literal")
|
||||
// }
|
||||
// }.toList()
|
||||
// }
|
||||
//
|
127
codeCore/src/prog8/code/ast/AstBase.kt
Normal file
127
codeCore/src/prog8/code/ast/AstBase.kt
Normal file
@ -0,0 +1,127 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SourceCode
|
||||
import java.nio.file.Path
|
||||
|
||||
// New simplified AST for the code generator.
|
||||
|
||||
|
||||
sealed class PtNode(val position: Position) {
|
||||
|
||||
val children = mutableListOf<PtNode>()
|
||||
lateinit var parent: PtNode
|
||||
|
||||
fun add(child: PtNode) {
|
||||
children.add(child)
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
fun add(index: Int, child: PtNode) {
|
||||
children.add(index, child)
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||
fun definingSub() = findParentNode<PtSub>(this)
|
||||
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||
fun definingISub() = findParentNode<IPtSubroutine>(this)
|
||||
}
|
||||
|
||||
|
||||
sealed interface IPtStatementContainer
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
|
||||
|
||||
|
||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||
// Note that as an exception, the 'name' is not read-only
|
||||
// but a var. This is to allow for cheap node renames.
|
||||
val scopedName: String
|
||||
get() {
|
||||
var namedParent: PtNode = this.parent
|
||||
return if(namedParent is PtProgram)
|
||||
name
|
||||
else {
|
||||
while (namedParent !is PtNamedNode)
|
||||
namedParent = namedParent.parent
|
||||
namedParent.scopedName + "." + name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtProgram(
|
||||
val name: String,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding
|
||||
) : PtNode(Position.DUMMY) {
|
||||
|
||||
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||
|
||||
fun allBlocks(): Sequence<PtBlock> =
|
||||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
|
||||
?.children
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
class PtBlock(name: String,
|
||||
val library: Boolean,
|
||||
val source: SourceCode, // taken from the module the block is defined in.
|
||||
val options: Options,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtStatementContainer {
|
||||
enum class BlockAlignment {
|
||||
NONE,
|
||||
WORD,
|
||||
PAGE
|
||||
}
|
||||
|
||||
class Options(val address: UInt? = null,
|
||||
val forceOutput: Boolean = false,
|
||||
val noSymbolPrefixing: Boolean = false,
|
||||
val veraFxMuls: Boolean = false,
|
||||
val ignoreUnused: Boolean = false,
|
||||
val alignment: BlockAlignment = BlockAlignment.NONE)
|
||||
}
|
||||
|
||||
|
||||
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
||||
init {
|
||||
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
||||
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
|
||||
|
||||
|
||||
class PtBreakpoint(position: Position): PtNode(position)
|
||||
|
||||
|
||||
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)
|
||||
|
||||
|
||||
class PtNop(position: Position): PtNode(position)
|
||||
|
||||
|
||||
// find the parent node of a specific type or interface
|
||||
// (useful to figure out in what namespace/block something is defined, etc.)
|
||||
inline fun <reified T> findParentNode(node: PtNode): T? {
|
||||
var candidate = node.parent
|
||||
while(candidate !is T && candidate !is PtProgram)
|
||||
candidate = candidate.parent
|
||||
return if(candidate is PtProgram)
|
||||
null
|
||||
else
|
||||
candidate as T
|
||||
}
|
334
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
334
codeCore/src/prog8/code/ast/AstExpressions.kt
Normal file
@ -0,0 +1,334 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.truncate
|
||||
|
||||
|
||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||
|
||||
init {
|
||||
if(type==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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(other: PtExpression): Boolean {
|
||||
return when(this) {
|
||||
is PtAddressOf -> {
|
||||
if(other !is PtAddressOf)
|
||||
return false
|
||||
if (other.type!==type || !(other.identifier isSameAs identifier))
|
||||
return false
|
||||
if(other.children.size!=children.size)
|
||||
return false
|
||||
if(children.size==1)
|
||||
return true
|
||||
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
||||
}
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||
is PtBinaryExpression -> {
|
||||
if(other !is PtBinaryExpression || other.operator!=operator)
|
||||
false
|
||||
else if(operator in AssociativeOperators)
|
||||
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
|
||||
else
|
||||
other.left isSameAs left && other.right isSameAs right
|
||||
}
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||
is 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
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(target: PtAssignTarget): Boolean {
|
||||
return when {
|
||||
target.memory != null && this is PtMemoryByte-> {
|
||||
target.memory!!.address isSameAs this.address
|
||||
}
|
||||
target.identifier != null && this is PtIdentifier -> {
|
||||
this.name == target.identifier!!.name
|
||||
}
|
||||
target.array != null && this is PtArrayIndexer -> {
|
||||
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
|
||||
|
||||
fun isSimple(): Boolean {
|
||||
return when(this) {
|
||||
is PtAddressOf -> true
|
||||
is PtArray -> true
|
||||
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||
is PtBinaryExpression -> false
|
||||
is PtBuiltinFunctionCall -> {
|
||||
when (name) {
|
||||
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is PtContainmentCheck -> false
|
||||
is PtFunctionCall -> false
|
||||
is PtIdentifier -> true
|
||||
is PtMachineRegister -> true
|
||||
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
|
||||
is PtNumber -> true
|
||||
is PtPrefix -> value.isSimple()
|
||||
is PtRange -> true
|
||||
is PtString -> true
|
||||
is PtTypeCast -> value.isSimple()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fun clone(): PtExpression {
|
||||
fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression {
|
||||
orig.children.forEach { clone.add((it as PtExpression).clone()) }
|
||||
return clone
|
||||
}
|
||||
when(this) {
|
||||
is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position))
|
||||
is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position))
|
||||
is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position))
|
||||
is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position))
|
||||
is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position))
|
||||
is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position))
|
||||
is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position))
|
||||
is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position))
|
||||
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||
is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position))
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||
val identifier: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val arrayIndexExpr: PtExpression?
|
||||
get() = if(children.size==2) children[1] as PtExpression else null
|
||||
|
||||
val isFromArrayElement: Boolean
|
||||
get() = children.size==2
|
||||
}
|
||||
|
||||
|
||||
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
|
||||
val variable: PtIdentifier
|
||||
get() {
|
||||
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
|
||||
return children[0] as PtIdentifier
|
||||
}
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
val size: Int
|
||||
get() = children.size
|
||||
}
|
||||
|
||||
|
||||
class PtBuiltinFunctionCall(val name: String,
|
||||
val void: Boolean,
|
||||
val hasNoSideEffects: Boolean,
|
||||
type: DataType,
|
||||
position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val args: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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 name: String,
|
||||
val void: Boolean,
|
||||
type: DataType,
|
||||
position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val args: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
}
|
||||
|
||||
|
||||
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
||||
override fun toString(): String {
|
||||
return "[PtIdentifier:$name $type $position]"
|
||||
}
|
||||
|
||||
fun copy() = PtIdentifier(name, type, position)
|
||||
}
|
||||
|
||||
|
||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||
val address: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
}
|
||||
|
||||
|
||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||
|
||||
companion object {
|
||||
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
|
||||
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
|
||||
}
|
||||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type!=DataType.FLOAT) {
|
||||
val trunc = truncate(number)
|
||||
if (trunc != number)
|
||||
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(type, number)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtNumber)
|
||||
return false
|
||||
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
|
||||
return number==other.number
|
||||
else
|
||||
return type==other.type && number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||
|
||||
override fun toString() = "PtNumber:$type:$number"
|
||||
}
|
||||
|
||||
|
||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
val value: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
|
||||
init {
|
||||
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtRange(type: DataType, position: Position) : PtExpression(type, position) {
|
||||
val from: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val to: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
val step: PtNumber
|
||||
get() = children[2] as PtNumber
|
||||
|
||||
fun toConstantIntegerRange(): IntProgression? {
|
||||
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||
return when {
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
stepVal == 1 -> fromVal..toVal
|
||||
else -> fromVal..toVal step stepVal
|
||||
}
|
||||
else -> when {
|
||||
stepVal >= 0 -> IntRange.EMPTY
|
||||
stepVal == -1 -> fromVal downTo toVal
|
||||
else -> fromVal downTo toVal step abs(stepVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val fromLv = from as? PtNumber
|
||||
val toLv = to as? PtNumber
|
||||
val stepLv = step as? PtNumber
|
||||
if(fromLv==null || toLv==null || stepLv==null)
|
||||
return null
|
||||
val fromVal = fromLv.number.toInt()
|
||||
val toVal = toLv.number.toInt()
|
||||
val stepVal = stepLv.number.toInt()
|
||||
return makeRange(fromVal, toVal, stepVal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
||||
override fun hashCode(): Int = Objects.hash(value, encoding)
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtString)
|
||||
return false
|
||||
return value==other.value && encoding == other.encoding
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtTypeCast(type: 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 in the Intermediate Code
|
||||
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||
|
||||
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
|
172
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
172
codeCore/src/prog8/code/ast/AstPrinter.kt
Normal file
@ -0,0 +1,172 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
/**
|
||||
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
||||
* passing it as a String to the specified receiver function.
|
||||
*/
|
||||
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
|
||||
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||
fun txt(node: PtNode): String {
|
||||
return when(node) {
|
||||
is PtAssignTarget -> "<target>"
|
||||
is PtAssignment -> "<assign>"
|
||||
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
|
||||
is PtBreakpoint -> "%breakpoint"
|
||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||
is PtAddressOf -> "&"
|
||||
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val str = if(node.void) "void " else ""
|
||||
str + node.name + "()"
|
||||
}
|
||||
is PtContainmentCheck -> "in"
|
||||
is PtFunctionCall -> {
|
||||
val str = if(node.void) "void " else ""
|
||||
str + node.name + "()"
|
||||
}
|
||||
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
||||
is PtMemoryByte -> "@()"
|
||||
is PtNumber -> {
|
||||
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
|
||||
"$numstr ${type(node.type)}"
|
||||
}
|
||||
is PtPrefix -> node.operator
|
||||
is PtRange -> "<range>"
|
||||
is PtString -> "\"${node.value.escape()}\""
|
||||
is PtTypeCast -> "as ${node.type.name.lowercase()}"
|
||||
is PtForLoop -> "for"
|
||||
is PtIfElse -> "ifelse"
|
||||
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
|
||||
is PtInlineAssembly -> {
|
||||
if(node.isIR)
|
||||
"%ir {{ ...${node.assembly.length} characters... }}"
|
||||
else
|
||||
"%asm {{ ...${node.assembly.length} characters... }}"
|
||||
}
|
||||
is PtJump -> {
|
||||
if(node.identifier!=null)
|
||||
"goto ${node.identifier.name}"
|
||||
else if(node.address!=null)
|
||||
"goto ${node.address.toHex()}"
|
||||
else
|
||||
"???"
|
||||
}
|
||||
is PtAsmSub -> {
|
||||
val params = node.parameters.map {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
|
||||
}.joinToString(", ")
|
||||
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||
val returns = if (node.returns.isEmpty()) "" else {
|
||||
"-> ${node.returns.map {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second} @${register ?: statusflag}"}
|
||||
.joinToString(", ")
|
||||
}"
|
||||
}
|
||||
val str = if (node.inline) "inline " else ""
|
||||
if(node.address==null) {
|
||||
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||
} else {
|
||||
str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns"
|
||||
}
|
||||
}
|
||||
is PtBlock -> {
|
||||
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}"
|
||||
val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}"
|
||||
"\nblock '${node.name}' $addr $align"
|
||||
}
|
||||
is PtConstant -> {
|
||||
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
|
||||
"const ${node.type.name.lowercase()} ${node.name} = $value"
|
||||
}
|
||||
is PtLabel -> "${node.name}:"
|
||||
is PtMemMapped -> {
|
||||
if(node.type in ArrayDatatypes) {
|
||||
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
|
||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
|
||||
} else {
|
||||
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
|
||||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ")
|
||||
var str = "sub ${node.name}($params) "
|
||||
if(node.returntype!=null)
|
||||
str += "-> ${node.returntype.name.lowercase()}"
|
||||
str
|
||||
}
|
||||
is PtVariable -> {
|
||||
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
|
||||
val str = if(node.arraySize!=null) {
|
||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
|
||||
}
|
||||
else if(node.type in ArrayDatatypes) {
|
||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||
"${eltType.name.lowercase()}[] $split ${node.name}"
|
||||
}
|
||||
else
|
||||
"${node.type.name.lowercase()} ${node.name}"
|
||||
if(node.value!=null)
|
||||
str + " = " + txt(node.value)
|
||||
else
|
||||
str
|
||||
}
|
||||
is PtNodeGroup -> "<group>"
|
||||
is PtNop -> "nop"
|
||||
is PtPostIncrDecr -> "<post> ${node.operator}"
|
||||
is PtProgram -> "PROGRAM ${node.name}"
|
||||
is PtRepeatLoop -> "repeat"
|
||||
is PtReturn -> "return"
|
||||
is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}"
|
||||
is PtWhen -> "when"
|
||||
is PtWhenChoice -> {
|
||||
if(node.isElse)
|
||||
"else"
|
||||
else
|
||||
"->"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(root is PtProgram) {
|
||||
output(txt(root))
|
||||
root.children.forEach {
|
||||
walkAst(it) { node, depth ->
|
||||
val txt = txt(node)
|
||||
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||
if(!library || !skipLibraries) {
|
||||
if (txt.isNotEmpty())
|
||||
output(" ".repeat(depth) + txt(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
println()
|
||||
} else {
|
||||
walkAst(root) { node, depth ->
|
||||
val txt = txt(node)
|
||||
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||
if(!library || !skipLibraries) {
|
||||
if (txt.isNotEmpty())
|
||||
output(" ".repeat(depth) + txt(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
|
||||
fun recurse(node: PtNode, depth: Int) {
|
||||
act(node, depth)
|
||||
node.children.forEach { recurse(it, depth+1) }
|
||||
}
|
||||
recurse(root, 0)
|
||||
}
|
171
codeCore/src/prog8/code/ast/AstStatements.kt
Normal file
171
codeCore/src/prog8/code/ast/AstStatements.kt
Normal file
@ -0,0 +1,171 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
sealed interface IPtSubroutine {
|
||||
val name: String
|
||||
}
|
||||
|
||||
class PtAsmSub(
|
||||
name: String,
|
||||
val address: UInt?,
|
||||
val clobbers: Set<CpuRegister>,
|
||||
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
||||
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
||||
val inline: Boolean,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine
|
||||
|
||||
|
||||
class PtSub(
|
||||
name: String,
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
if(parameters.any{ it.type !in NumericDatatypes })
|
||||
throw AssemblyError("non-numeric parameter")
|
||||
if(returntype!=null && returntype !in NumericDatatypes)
|
||||
throw AssemblyError("non-numeric returntype $returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
||||
|
||||
|
||||
sealed interface IPtAssignment {
|
||||
val children: MutableList<PtNode>
|
||||
val target: PtAssignTarget
|
||||
get() = children[0] as PtAssignTarget
|
||||
val value: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
}
|
||||
|
||||
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
|
||||
}
|
||||
|
||||
|
||||
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
|
||||
val trueScope: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val falseScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtForLoop(position: Position) : PtNode(position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val iterable: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtIfElse(position: Position) : PtNode(position) {
|
||||
val condition: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val ifScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
|
||||
val address: UInt?,
|
||||
position: Position) : PtNode(position) {
|
||||
init {
|
||||
identifier?.let {it.parent = this }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
||||
val target: PtAssignTarget
|
||||
get() = children.single() as PtAssignTarget
|
||||
}
|
||||
|
||||
|
||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||
val count: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtReturn(position: Position) : PtNode(position) {
|
||||
val hasValue = children.any()
|
||||
val value: PtExpression?
|
||||
get() {
|
||||
return if(children.any())
|
||||
children.single() as PtExpression
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sealed interface IPtVariable {
|
||||
val name: String
|
||||
val type: DataType
|
||||
}
|
||||
|
||||
|
||||
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
value?.let {it.parent=this}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
val value: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val choices: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
||||
val values: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
135
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
135
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
@ -0,0 +1,135 @@
|
||||
package prog8.code.core
|
||||
|
||||
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||
class ParamConvention(val dt: DataType, 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!=null -> returns.reg.toString()
|
||||
returns.floatFac1 -> "floatFAC1"
|
||||
else -> "<no returnvalue>"
|
||||
}
|
||||
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||
}
|
||||
}
|
||||
|
||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
||||
|
||||
class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<FParam>,
|
||||
val returnType: DataType?) {
|
||||
|
||||
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
||||
val returns: ReturnConvention = when (returnType) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||
null -> ReturnConvention(null, null, false)
|
||||
else -> {
|
||||
// return type depends on arg type
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ReturnConvention(paramType, null, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||
actualParamTypes.size==1 -> {
|
||||
// one parameter goes via register/registerpair
|
||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
}
|
||||
CallConvention(listOf(paramConv), returns)
|
||||
}
|
||||
else -> {
|
||||
// multiple parameters go via variables
|
||||
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||
CallConvention(paramConvs, returns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
||||
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||
// normal functions follow:
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
|
||||
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
||||
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
||||
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
|
||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
|
||||
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse")
|
39
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
39
codeCore/src/prog8/code/core/CompilationOptions.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
|
||||
|
||||
class CompilationOptions(val output: OutputType,
|
||||
val launcher: CbmPrgLauncherType,
|
||||
val zeropage: ZeropageType,
|
||||
val zpReserved: List<UIntRange>,
|
||||
val zpAllowed: List<UIntRange>,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var warnSymbolShadowing: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var varsGolden: Boolean = false,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
init {
|
||||
compTarget.machine.initializeMemoryAreas(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
|
||||
}
|
||||
}
|
89
codeCore/src/prog8/code/core/Conversions.kt
Normal file
89
codeCore/src/prog8/code/core/Conversions.kt
Normal file
@ -0,0 +1,89 @@
|
||||
package prog8.code.core
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
fun Number.toHex(): String {
|
||||
// 0..15 -> "0".."15"
|
||||
// 16..255 -> "$10".."$ff"
|
||||
// 256..65536 -> "$0100".."$ffff"
|
||||
// negative values are prefixed with '-'.
|
||||
val integer = this.toInt()
|
||||
if(integer<0)
|
||||
return '-' + abs(integer).toHex()
|
||||
return when (integer) {
|
||||
in 0 until 16 -> integer.toString()
|
||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
|
||||
else -> throw IllegalArgumentException("number too large for 16 bits $this")
|
||||
}
|
||||
}
|
||||
|
||||
fun UInt.toHex(): String {
|
||||
// 0..15 -> "0".."15"
|
||||
// 16..255 -> "$10".."$ff"
|
||||
// 256..65536 -> "$0100".."$ffff"
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
fun Char.escape(): Char = this.toString().escape()[0]
|
||||
|
||||
fun String.escape(): String {
|
||||
val es = this.map {
|
||||
when(it) {
|
||||
'\t' -> "\\t"
|
||||
'\n' -> "\\n"
|
||||
'\r' -> "\\r"
|
||||
'"' -> "\\\""
|
||||
in '\u8000'..'\u80ff' -> "\\x" + (it.code - 0x8000).toString(16).padStart(2, '0') // 'ugly' passthrough hack
|
||||
in '\u0000'..'\u00ff' -> it.toString()
|
||||
else -> "\\u" + it.code.toString(16).padStart(4, '0')
|
||||
}
|
||||
}
|
||||
return es.joinToString("")
|
||||
}
|
||||
|
||||
fun String.unescape(): String {
|
||||
val result = mutableListOf<Char>()
|
||||
val iter = this.iterator()
|
||||
while(iter.hasNext()) {
|
||||
val c = iter.nextChar()
|
||||
if(c=='\\') {
|
||||
val ec = iter.nextChar()
|
||||
result.add(when(ec) {
|
||||
'\\' -> '\\'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
'"' -> '"'
|
||||
'\'' -> '\''
|
||||
'u' -> {
|
||||
try {
|
||||
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||
} catch (sb: StringIndexOutOfBoundsException) {
|
||||
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||
} catch (nf: NumberFormatException) {
|
||||
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||
}
|
||||
}
|
||||
'x' -> {
|
||||
try {
|
||||
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||
(0x8000 + hex).toChar() // 'ugly' pass-through hack
|
||||
} catch (sb: StringIndexOutOfBoundsException) {
|
||||
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||
} catch (nf: NumberFormatException) {
|
||||
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid escape char in string: \\$ec")
|
||||
})
|
||||
} else {
|
||||
result.add(c)
|
||||
}
|
||||
}
|
||||
return result.joinToString("")
|
||||
}
|
204
codeCore/src/prog8/code/core/Enumerations.kt
Normal file
204
codeCore/src/prog8/code/core/Enumerations.kt
Normal file
@ -0,0 +1,204 @@
|
||||
package prog8.code.core
|
||||
|
||||
enum class DataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
BYTE, // pass by value 8 bits signed
|
||||
UWORD, // pass by value 16 bits unsigned
|
||||
WORD, // pass by value 16 bits signed
|
||||
LONG, // pass by value 32 bits signed
|
||||
FLOAT, // pass by value machine dependent
|
||||
BOOL, // pass by value bit 0 of a 8 bit byte
|
||||
STR, // pass by reference
|
||||
ARRAY_UB, // pass by reference
|
||||
ARRAY_B, // pass by reference
|
||||
ARRAY_UW, // pass by reference
|
||||
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
|
||||
ARRAY_W, // pass by reference
|
||||
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
|
||||
ARRAY_F, // pass by reference
|
||||
ARRAY_BOOL, // pass by reference
|
||||
UNDEFINED;
|
||||
|
||||
/**
|
||||
* 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, LONG, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
|
||||
LONG -> targetType.oneOf(LONG, FLOAT)
|
||||
FLOAT -> targetType.oneOf(FLOAT)
|
||||
STR -> targetType.oneOf(STR, UWORD)
|
||||
in ArrayDatatypes -> targetType == this
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun oneOf(vararg types: DataType) = this in types
|
||||
|
||||
infix fun largerThan(other: DataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this in ByteDatatypes -> false
|
||||
this in WordDatatypes -> other in ByteDatatypes
|
||||
this == LONG -> other in ByteDatatypes+WordDatatypes
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
infix fun equalsSize(other: DataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this in ByteDatatypes -> other in ByteDatatypes
|
||||
this in WordDatatypes -> other in WordDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
enum class CpuRegister {
|
||||
A,
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
enum class RegisterOrPair {
|
||||
A,
|
||||
X,
|
||||
Y,
|
||||
AX,
|
||||
AY,
|
||||
XY,
|
||||
FAC1,
|
||||
FAC2,
|
||||
// cx16 virtual registers:
|
||||
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
|
||||
return when(cpu) {
|
||||
CpuRegister.A -> A
|
||||
CpuRegister.X -> X
|
||||
CpuRegister.Y -> Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun asCpuRegister(): CpuRegister = when(this) {
|
||||
A -> CpuRegister.A
|
||||
X -> CpuRegister.X
|
||||
Y -> CpuRegister.Y
|
||||
else -> throw IllegalArgumentException("no cpu hardware register for $this")
|
||||
}
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
Pc,
|
||||
Pz, // don't use
|
||||
Pv,
|
||||
Pn; // don't use
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
}
|
||||
}
|
||||
|
||||
enum class BranchCondition {
|
||||
CS,
|
||||
CC,
|
||||
EQ, // EQ == Z
|
||||
Z,
|
||||
NE, // NE == NZ
|
||||
NZ,
|
||||
MI, // MI == NEG
|
||||
NEG,
|
||||
PL, // PL == POS
|
||||
POS,
|
||||
VS,
|
||||
VC
|
||||
}
|
||||
|
||||
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
|
||||
val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val NumericDatatypes = NumericDatatypesNoBool + DataType.BOOL
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
|
||||
val IterableDatatypes = arrayOf(
|
||||
DataType.STR,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||
)
|
||||
val PassByValueDatatypes = 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_W_SPLIT to DataType.WORD,
|
||||
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
|
||||
DataType.ARRAY_F to DataType.FLOAT,
|
||||
DataType.ARRAY_BOOL to DataType.BOOL
|
||||
)
|
||||
val ElementToArrayTypes = mapOf(
|
||||
DataType.BYTE to DataType.ARRAY_B,
|
||||
DataType.UBYTE to DataType.ARRAY_UB,
|
||||
DataType.WORD to DataType.ARRAY_W,
|
||||
DataType.UWORD to DataType.ARRAY_UW,
|
||||
DataType.FLOAT to DataType.ARRAY_F,
|
||||
DataType.BOOL to DataType.ARRAY_BOOL,
|
||||
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
|
||||
)
|
||||
|
||||
val Cx16VirtualRegisters = arrayOf(
|
||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
||||
RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11,
|
||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||
)
|
||||
|
||||
val CpuRegisters = setOf(
|
||||
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||
)
|
||||
|
||||
|
||||
enum class OutputType {
|
||||
RAW,
|
||||
PRG,
|
||||
XEX
|
||||
}
|
||||
|
||||
enum class CbmPrgLauncherType {
|
||||
BASIC,
|
||||
NONE
|
||||
}
|
||||
|
||||
enum class ZeropageType {
|
||||
BASICSAFE,
|
||||
FLOATSAFE,
|
||||
KERNALSAFE,
|
||||
FULL,
|
||||
DONTUSE
|
||||
}
|
||||
|
||||
enum class ZeropageWish {
|
||||
REQUIRE_ZEROPAGE,
|
||||
PREFER_ZEROPAGE,
|
||||
DONTCARE,
|
||||
NOT_IN_ZEROPAGE
|
||||
}
|
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
7
codeCore/src/prog8/code/core/Exceptions.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package prog8.code.core
|
||||
|
||||
class InternalCompilerException(message: String?) : Exception(message)
|
||||
|
||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||
|
||||
class ErrorsReportedException(message: String?) : Exception(message)
|
17
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
17
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
|
||||
interface ICodeGeneratorBackend {
|
||||
fun generate(program: PtProgram,
|
||||
symbolTable: SymbolTable,
|
||||
options: CompilationOptions,
|
||||
errors: IErrorReporter): IAssemblyProgram?
|
||||
}
|
||||
|
||||
|
||||
interface IAssemblyProgram {
|
||||
val name: String
|
||||
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
|
||||
}
|
9
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
9
codeCore/src/prog8/code/core/ICompilationTarget.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package prog8.code.core
|
||||
|
||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
val name: String
|
||||
val machine: IMachineDefinition
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
}
|
14
codeCore/src/prog8/code/core/IErrorReporter.kt
Normal file
14
codeCore/src/prog8/code/core/IErrorReporter.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package prog8.code.core
|
||||
|
||||
interface IErrorReporter {
|
||||
fun err(msg: String, position: Position)
|
||||
fun warn(msg: String, position: Position)
|
||||
fun info(msg: String, position: Position)
|
||||
fun undefined(symbol: List<String>, position: Position)
|
||||
fun noErrors(): Boolean
|
||||
fun report()
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||
if(numErrors>0)
|
||||
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||
}
|
||||
}
|
32
codeCore/src/prog8/code/core/IMachineDefinition.kt
Normal file
32
codeCore/src/prog8/code/core/IMachineDefinition.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
var golden: GoldenRam
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
}
|
6
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
6
codeCore/src/prog8/code/core/IMemSizer.kt
Normal file
@ -0,0 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
interface IMemSizer {
|
||||
fun memorySize(dt: DataType): Int
|
||||
fun memorySize(arrayDt: DataType, numElements: Int): Int
|
||||
}
|
16
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
16
codeCore/src/prog8/code/core/IStringEncoding.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package prog8.code.core
|
||||
|
||||
enum class Encoding(val prefix: String) {
|
||||
DEFAULT("default"), // depends on compilation target
|
||||
PETSCII("petscii"), // c64/c128/cx16
|
||||
SCREENCODES("sc"), // c64/c128/cx16
|
||||
ATASCII("atascii"), // atari
|
||||
ISO("iso") // cx16
|
||||
}
|
||||
|
||||
interface IStringEncoding {
|
||||
val defaultEncoding: Encoding
|
||||
|
||||
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
}
|
173
codeCore/src/prog8/code/core/MemoryRegions.kt
Normal file
173
codeCore/src/prog8/code/core/MemoryRegions.kt
Normal file
@ -0,0 +1,173 @@
|
||||
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, 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(setOf(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) {
|
||||
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(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if(free.size > 0) {
|
||||
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) {
|
||||
in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
DataType.STR -> VarAllocation(address, datatype, size)
|
||||
in ArrayDatatypes -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
options.compTarget.memorySize(datatype, numElements!!)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (options.floats) {
|
||||
options.compTarget.memorySize(DataType.FLOAT)
|
||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
}
|
||||
|
||||
return if(nextLocation<=region.last && (region.last + 1u - nextLocation) >= size.toUInt()) {
|
||||
val result = Ok(VarAllocation(nextLocation, datatype, size))
|
||||
nextLocation += size.toUInt()
|
||||
result
|
||||
} else
|
||||
Err(MemAllocationError("no more free space in Golden RAM to allocate $size sequential bytes"))
|
||||
}
|
||||
}
|
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
18
codeCore/src/prog8/code/core/Operators.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
|
||||
fun invertedComparisonOperator(operator: String) =
|
||||
when (operator) {
|
||||
"==" -> "!="
|
||||
"!=" -> "=="
|
||||
"<" -> ">="
|
||||
">" -> "<="
|
||||
"<=" -> ">"
|
||||
">=" -> "<"
|
||||
else -> null
|
||||
}
|
27
codeCore/src/prog8/code/core/Position.kt
Normal file
27
codeCore/src/prog8/code/core/Position.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
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(file.startsWith(LIBRARYFILEPREFIX))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(x: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
"file://$file:$line:$startCol:"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DUMMY = Position("~dummy~", 0, 0, 0)
|
||||
}
|
||||
}
|
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
3
codeCore/src/prog8/code/core/RegisterOrStatusflag.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package prog8.code.core
|
||||
|
||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?)
|
177
codeCore/src/prog8/code/core/SourceCode.kt
Normal file
177
codeCore/src/prog8/code/core/SourceCode.kt
Normal file
@ -0,0 +1,177 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates - and ties together - actual source code (=text) and its [origin].
|
||||
*/
|
||||
sealed class SourceCode {
|
||||
|
||||
/**
|
||||
* Whether this [SourceCode] instance was created as a [Resource]
|
||||
*/
|
||||
abstract val isFromResources: Boolean
|
||||
|
||||
/**
|
||||
* Whether this [SourceCode] instance was created as a [File]
|
||||
*/
|
||||
abstract val isFromFilesystem: Boolean
|
||||
|
||||
/**
|
||||
* The logical name of the source code unit. Usually the module's name.
|
||||
*/
|
||||
abstract val name: String
|
||||
|
||||
/**
|
||||
* Where this [SourceCode] instance came from.
|
||||
* This can be one of the following:
|
||||
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
|
||||
* * `string:44c56085` if was created via [String]
|
||||
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
|
||||
*/
|
||||
abstract val origin: String
|
||||
|
||||
/**
|
||||
* The source code as plain string.
|
||||
*/
|
||||
abstract val text: String
|
||||
|
||||
/**
|
||||
* Printable representation, deliberately does NOT return the actual text.
|
||||
*/
|
||||
final override fun toString() = "${this.javaClass.name}[${this.origin}]"
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||
*/
|
||||
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))
|
||||
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `string:44c56085`.
|
||||
*/
|
||||
class Text(origText: String): SourceCode() {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val 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.
|
||||
*
|
||||
* [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() {
|
||||
override val text: String
|
||||
override val origin: String
|
||||
override val name: String
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = true
|
||||
|
||||
init {
|
||||
val normalized = path.normalize()
|
||||
origin = relative(normalized).toString()
|
||||
try {
|
||||
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = normalized.toFile().nameWithoutExtension
|
||||
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(normalized.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
|
||||
*/
|
||||
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 text: String
|
||||
override val name: String
|
||||
|
||||
init {
|
||||
val rscURL = object {}.javaClass.getResource(normalized)
|
||||
if (rscURL == null) {
|
||||
val rscRoot = object {}.javaClass.getResource("/")
|
||||
throw NoSuchFileException(
|
||||
File(normalized),
|
||||
reason = "looked in resources rooted at $rscRoot"
|
||||
)
|
||||
}
|
||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = Path(pathString).toFile().nameWithoutExtension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SourceCode for internally generated nodes (usually Modules)
|
||||
*/
|
||||
class Generated(override val name: String) : SourceCode() {
|
||||
override val isFromResources: Boolean = false
|
||||
override val isFromFilesystem: Boolean = false
|
||||
override val origin: String = name
|
||||
override val text: String = "<generated code node, no text representation>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object SourceLineCache {
|
||||
private val cache = mutableMapOf<String, List<String>>()
|
||||
|
||||
private fun getCachedFile(file: String): List<String> {
|
||||
val existing = cache[file]
|
||||
if(existing!=null)
|
||||
return existing
|
||||
if (SourceCode.isRegularFilesystemPath(file)) {
|
||||
val source = SourceCode.File(Path(file))
|
||||
cache[file] = source.text.split('\n', '\r').map { it.trim() }
|
||||
return cache.getValue(file)
|
||||
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
|
||||
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
|
||||
cache[file] = source.text.split('\n', '\r').map { it.trim()}
|
||||
return cache.getValue(file)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
fun retrieveLine(position: Position): String? {
|
||||
if (position.line>0) {
|
||||
val lines = getCachedFile(position.file)
|
||||
if(lines.isNotEmpty())
|
||||
return lines[position.line-1]
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
130
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
130
codeCore/src/prog8/code/optimize/Optimizer.kt
Normal file
@ -0,0 +1,130 @@
|
||||
package prog8.code.optimize
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) {
|
||||
if (!options.optimize)
|
||||
return
|
||||
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) {
|
||||
// keep rolling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
fun recurse(node: PtNode, depth: Int) {
|
||||
if(act(node, depth))
|
||||
node.children.forEach { recurse(it, depth+1) }
|
||||
}
|
||||
recurse(root, 0)
|
||||
}
|
||||
|
||||
|
||||
private var tempVarCounter = 0
|
||||
|
||||
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
|
||||
|
||||
fun extractableSubExpr(expr: PtExpression): Boolean {
|
||||
val result = if(expr is PtBinaryExpression)
|
||||
expr.type !in ByteDatatypes ||
|
||||
!expr.left.isSimple() ||
|
||||
!expr.right.isSimple() ||
|
||||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
|
||||
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
|
||||
true
|
||||
else
|
||||
!expr.isSimple()
|
||||
return result
|
||||
}
|
||||
|
||||
// for each Binaryexpression, recurse to find a common subexpression pair therein.
|
||||
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtBinaryExpression) {
|
||||
val subExpressions = mutableListOf<PtExpression>()
|
||||
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
outer@for (first in subExpressions) {
|
||||
for (second in subExpressions) {
|
||||
if (first!==second && first isSameAs second) {
|
||||
commons[node] = first to second
|
||||
break@outer // do only 1 replacement at a time per binaryexpression
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
} else true
|
||||
}
|
||||
|
||||
// replace common subexpressions by a temp variable that is assigned only once.
|
||||
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
|
||||
commons.forEach { binexpr, (occurrence1, occurrence2) ->
|
||||
val (stmtContainer, stmt) = findContainingStatements(binexpr)
|
||||
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
|
||||
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
|
||||
val containerScopedName = findScopeName(stmtContainer)
|
||||
tempVarCounter++
|
||||
val tempvarName = "prog8_subexprvar_$tempVarCounter"
|
||||
// TODO: some tempvars could be reused, if they are from different lines
|
||||
|
||||
val datatype = occurrence1.type
|
||||
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
|
||||
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
|
||||
occurrence1.parent.children[occurrence1idx] = singleReplacement1
|
||||
singleReplacement1.parent = occurrence1.parent
|
||||
occurrence2.parent.children[occurrence2idx] = singleReplacement2
|
||||
singleReplacement2.parent = occurrence2.parent
|
||||
|
||||
val tempassign = PtAssignment(binexpr.position).also { assign ->
|
||||
assign.add(PtAssignTarget(binexpr.position).also { tgt->
|
||||
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
|
||||
})
|
||||
assign.add(occurrence1)
|
||||
occurrence1.parent = assign
|
||||
}
|
||||
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
|
||||
tempassign.parent = stmtContainer
|
||||
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
|
||||
stmtContainer.add(0, tempvar)
|
||||
tempvar.parent = stmtContainer
|
||||
|
||||
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
|
||||
}
|
||||
|
||||
return commons.size
|
||||
}
|
||||
|
||||
|
||||
internal fun findScopeName(node: PtNode): String {
|
||||
var parent=node
|
||||
while(parent !is PtNamedNode)
|
||||
parent = parent.parent
|
||||
return parent.scopedName
|
||||
}
|
||||
|
||||
|
||||
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
|
||||
var parent = node.parent
|
||||
var child = node
|
||||
while(true) {
|
||||
if(parent is IPtStatementContainer) {
|
||||
return parent to child
|
||||
}
|
||||
child=parent
|
||||
parent=parent.parent
|
||||
}
|
||||
}
|
27
codeCore/src/prog8/code/target/AtariTarget.kt
Normal file
27
codeCore/src/prog8/code/target/AtariTarget.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.atari.AtariMachineDefinition
|
||||
|
||||
|
||||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = AtariMachineDefinition()
|
||||
override val defaultEncoding = Encoding.ATASCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "atari"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
19
codeCore/src/prog8/code/target/C128Target.kt
Normal file
19
codeCore/src/prog8/code/target/C128Target.kt
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = C128MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
}
|
||||
}
|
21
codeCore/src/prog8/code/target/C64Target.kt
Normal file
21
codeCore/src/prog8/code/target/C64Target.kt
Normal file
@ -0,0 +1,21 @@
|
||||
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
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = C64MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "c64"
|
||||
|
||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||
}
|
||||
}
|
19
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
19
codeCore/src/prog8/code/target/Cx16Target.kt
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = CX16MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
}
|
||||
}
|
41
codeCore/src/prog8/code/target/Encoder.kt
Normal file
41
codeCore/src/prog8/code/target/Encoder.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package prog8.code.target
|
||||
|
||||
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
|
||||
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return coded.fold(
|
||||
failure = { throw it },
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||
val decoded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return decoded.fold(
|
||||
failure = { throw it },
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
}
|
19
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
19
codeCore/src/prog8/code/target/PETTarget.kt
Normal file
@ -0,0 +1,19 @@
|
||||
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.pet.PETMachineDefinition
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = PETMachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
}
|
||||
}
|
26
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
26
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = VirtualMachineDefinition()
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class AtariMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
||||
|
||||
override fun 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 initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = AtariZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
}
|
57
codeCore/src/prog8/code/target/atari/AtariZeropage.kt
Normal file
57
codeCore/src/prog8/code/target/atari/AtariZeropage.kt
Normal file
@ -0,0 +1,57 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
||||
free.addAll(0x00u..0xffu)
|
||||
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
}
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if atari can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
56
codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
Normal file
56
codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package prog8.code.target.c128
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun 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 = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||
}
|
||||
}
|
78
codeCore/src/prog8/code/target/c128/C128Zeropage.kt
Normal file
78
codeCore/src/prog8/code/target/c128/C128Zeropage.kt
Normal file
@ -0,0 +1,78 @@
|
||||
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
|
||||
|
||||
|
||||
// reference: "Mapping the C128" zero page 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, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
||||
free.addAll(0x0au..0xffu)
|
||||
free.removeAll(listOf(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(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
||||
free.addAll(0x1bu..0x23u)
|
||||
free.addAll(listOf(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(listOf(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()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if C128 can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
66
codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
Normal file
66
codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
Normal file
@ -0,0 +1,66 @@
|
||||
package prog8.code.target.c64
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xcfffu
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun 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 = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
||||
}
|
||||
|
||||
}
|
98
codeCore/src/prog8/code/target/c64/C64Zeropage.kt
Normal file
98
codeCore/src/prog8/code/target/c64/C64Zeropage.kt
Normal file
@ -0,0 +1,98 @@
|
||||
package prog8.code.target.c64
|
||||
|
||||
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_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(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
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
free.addAll(listOf(
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
// 0x90-0xfa is 'kernal work storage area'
|
||||
).map{it.toUInt()})
|
||||
}
|
||||
|
||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
// remove the zeropage locations used for floating point operations from the free list
|
||||
free.removeAll(listOf(
|
||||
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
||||
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
).map{it.toUInt()})
|
||||
}
|
||||
|
||||
if(options.zeropage != ZeropageType.DONTUSE) {
|
||||
// add the free Zp addresses
|
||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||
} else {
|
||||
// don't use the zeropage at all
|
||||
free.clear()
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
|
||||
allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.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())
|
||||
}
|
||||
}
|
||||
}
|
214
codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt
Normal file
214
codeCore/src/prog8/code/target/cbm/AtasciiEncoding.kt
Normal file
@ -0,0 +1,214 @@
|
||||
package prog8.code.target.cbm
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
|
||||
object AtasciiEncoding {
|
||||
|
||||
private val decodeTable: CharArray = charArrayOf(
|
||||
// $00
|
||||
'♥',
|
||||
'├',
|
||||
'\uf130', // 🮇 0x02 -> RIGHT ONE QUARTER BLOCK (CUS)
|
||||
'┘',
|
||||
'┤',
|
||||
'┐',
|
||||
'╱',
|
||||
'╲',
|
||||
'◢',
|
||||
'▗',
|
||||
'◣',
|
||||
'▝',
|
||||
'▘',
|
||||
'\uf132', // 🮂 0x1d -> UPPER ONE QUARTER BLOCK (CUS)
|
||||
'▂',
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
'•',
|
||||
'▄',
|
||||
'▎',
|
||||
'┬',
|
||||
'┴',
|
||||
'▌',
|
||||
'└',
|
||||
'\u001b', // $1b = escape
|
||||
'\ufffe', // UNDEFINED CHAR. $1c = cursor up
|
||||
'\ufffe', // UNDEFINED CHAR. $1d = cursor down
|
||||
'\ufffe', // UNDEFINED CHAR. $1e = cursor left
|
||||
'\ufffe', // UNDEFINED CHAR. $1f = cursor right
|
||||
|
||||
// $20
|
||||
' ',
|
||||
'!',
|
||||
'"',
|
||||
'#',
|
||||
'$',
|
||||
'%',
|
||||
'&',
|
||||
'\'',
|
||||
'(',
|
||||
')',
|
||||
'*',
|
||||
'+',
|
||||
',',
|
||||
'-',
|
||||
'.',
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
':',
|
||||
';',
|
||||
'<',
|
||||
'=',
|
||||
'>',
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
|
||||
// $50
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
'[',
|
||||
'\\',
|
||||
']',
|
||||
'^',
|
||||
'_',
|
||||
|
||||
// $60
|
||||
'♦',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
|
||||
// $70
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'♠',
|
||||
'|',
|
||||
'\u000c', // $7d -> FORM FEED (CLEAR SCREEN)
|
||||
'\u0008', // $7e -> BACKSPACE
|
||||
'\u0009', // $7f -> TAB
|
||||
|
||||
// $80-$ff are reversed video characters + various special characters.
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $90
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\n', // $9b -> EOL/RETURN
|
||||
'\ufffe', // UNDEFINED $9c = DELETE LINE
|
||||
'\ufffe', // UNDEFINED $9d = INSERT LINE
|
||||
'\ufffe', // UNDEFINED $9e = CLEAR TAB STOP
|
||||
'\ufffe', // UNDEFINED $9f = SET TAB STOP
|
||||
// $a0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $b0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $c0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $d0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $e0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
// $f0
|
||||
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\ufffe',
|
||||
'\u0007', // $fd = bell/beep
|
||||
'\u007f', // $fe = DELETE
|
||||
'\ufffe' // UNDEFINED $ff = INSERT
|
||||
)
|
||||
|
||||
private val encodeTable = decodeTable.withIndex().associate{it.value to it.index}
|
||||
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> encodeTable.getValue(chr).toUByte()
|
||||
}
|
||||
}
|
||||
return Ok(mapped)
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
||||
}
|
||||
}
|
18
codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
Normal file
18
codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
Normal file
@ -0,0 +1,18 @@
|
||||
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 -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||
}
|
37
codeCore/src/prog8/code/target/cbm/IsoEncoding.kt
Normal file
37
codeCore/src/prog8/code/target/cbm/IsoEncoding.kt
Normal file
@ -0,0 +1,37 @@
|
||||
package prog8.code.target.cbm
|
||||
|
||||
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 IsoEncoding {
|
||||
val charset: Charset = Charset.forName("ISO-8859-15")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> charset.encode(chr.toString())[0].toUByte()
|
||||
}
|
||||
}
|
||||
Ok(mapped)
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
77
codeCore/src/prog8/code/target/cbm/Mflpt5.kt
Normal file
77
codeCore/src/prog8/code/target/cbm/Mflpt5.kt
Normal file
@ -0,0 +1,77 @@
|
||||
package prog8.code.target.cbm
|
||||
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||
|
||||
companion object {
|
||||
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
const val FLOAT_MEM_SIZE = 5
|
||||
|
||||
val zero = Mflpt5(0u, 0u, 0u, 0u, 0u)
|
||||
fun fromNumber(num: Number): Mflpt5 {
|
||||
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
||||
val sign = if (flt < 0.0) 0x80L else 0x00L
|
||||
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
||||
var mantissa = flt.absoluteValue
|
||||
|
||||
// if mantissa is too large, shift right and adjust exponent
|
||||
while (mantissa >= 0x100000000) {
|
||||
mantissa /= 2.0
|
||||
exponent++
|
||||
}
|
||||
// if mantissa is too small, shift left and adjust exponent
|
||||
while (mantissa < 0x80000000) {
|
||||
mantissa *= 2.0
|
||||
exponent--
|
||||
}
|
||||
|
||||
return when {
|
||||
exponent < 0 -> zero // underflow, use zero instead
|
||||
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||
exponent == 0 -> zero
|
||||
else -> {
|
||||
val mantLong = mantissa.toLong()
|
||||
Mflpt5(
|
||||
exponent.toUByte(),
|
||||
(mantLong.and(0x7f000000L) ushr 24).or(sign).toUByte(),
|
||||
(mantLong.and(0x00ff0000L) ushr 16).toUByte(),
|
||||
(mantLong.and(0x0000ff00L) ushr 8).toUByte(),
|
||||
(mantLong.and(0x000000ffL)).toUByte()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toDouble(): Double {
|
||||
if (this == zero) return 0.0
|
||||
val exp = b0.toInt() - 128
|
||||
val sign = (b1.toInt() and 0x80) > 0
|
||||
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||
return if (sign) -result else result
|
||||
}
|
||||
|
||||
fun makeFloatFillAsm(): String {
|
||||
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||
return "$b0, $b1, $b2, $b3, $b4"
|
||||
}
|
||||
}
|
1193
codeCore/src/prog8/code/target/cbm/PetsciiEncoding.kt
Normal file
1193
codeCore/src/prog8/code/target/cbm/PetsciiEncoding.kt
Normal file
File diff suppressed because it is too large
Load Diff
68
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
68
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,68 @@
|
||||
package prog8.code.target.cx16
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
override val BSSGOLDENRAM_START = 0x0400u
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
override fun 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 = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
||||
}
|
||||
|
||||
}
|
68
codeCore/src/prog8/code/target/cx16/CX16Zeropage.kt
Normal file
68
codeCore/src/prog8/code/target/cx16/CX16Zeropage.kt
Normal file
@ -0,0 +1,68 @@
|
||||
package prog8.code.target.cx16
|
||||
|
||||
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_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||
|
||||
synchronized(this) {
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x22u..0xffu)
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xa9u..0xffu)
|
||||
}
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xd4u..0xffu)
|
||||
}
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.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
|
||||
}
|
||||
}
|
||||
}
|
57
codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt
Normal file
57
codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt
Normal file
@ -0,0 +1,57 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u
|
||||
override val BSSHIGHRAM_END = 0u
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun 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 pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
// there's no golden ram.
|
||||
}
|
||||
|
||||
}
|
59
codeCore/src/prog8/code/target/pet/PETZeropage.kt
Normal file
59
codeCore/src/prog8/code/target/pet/PETZeropage.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
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, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if PET can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package prog8.code.target.virtual
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number): String {
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
override fun 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 in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
}
|
||||
}
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
override val SCRATCH_B1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_REG: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
|
||||
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
|
||||
}
|
63
codeGenCpu6502/build.gradle
Normal file
63
codeGenCpu6502/build.gradle
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "io.kotest" version "0.3.9"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDir "${project.projectDir}/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
// Enable JUnit 5 (Gradle 4.6+).
|
||||
useJUnitPlatform()
|
||||
|
||||
// Always run tests, even when nothing changed.
|
||||
dependsOn 'cleanTest'
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events "skipped", "failed"
|
||||
}
|
||||
}
|
18
codeGenCpu6502/codeGenCpu6502.iml
Normal file
18
codeGenCpu6502/codeGenCpu6502.iml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<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="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" />
|
||||
</component>
|
||||
</module>
|
3171
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
3171
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
535
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
535
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt
Normal file
@ -0,0 +1,535 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.IMachineDefinition
|
||||
|
||||
|
||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||
|
||||
|
||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
||||
|
||||
var numberOfOptimizations = 0
|
||||
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeUselessPushPopStack(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeUnneededTempvarInAdd(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
var linesByFourteen = getLinesBy(lines, 14)
|
||||
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeSamePointerIndexing(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
// TODO more assembly peephole optimizations
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
private fun String.isBranch() = this.startsWith("b")
|
||||
private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty") || this.startsWith("stx")
|
||||
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||
|
||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||
if(modification.remove)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else
|
||||
lines[modification.lineIndex] = modification.replacement!!
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
|
||||
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||
// The better place to do this is in the Compiler instead and never create these types of assembly, but hey
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFourteen) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
val fifth = lines[4].value.trimStart()
|
||||
val sixth = lines[5].value.trimStart()
|
||||
val seventh = lines[6].value.trimStart()
|
||||
val eighth = lines[7].value.trimStart()
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = fifth.substring(4)
|
||||
val fourthvalue = sixth.substring(4)
|
||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||
val address1 = getAddressArg(first, symbolTable)
|
||||
val address2 = getAddressArg(second, symbolTable)
|
||||
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
|
||||
mods.add(Modification(lines[4].index, true, null))
|
||||
mods.add(Modification(lines[5].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue) {
|
||||
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
val nineth = lines[8].value.trimStart()
|
||||
val tenth = lines[9].value.trimStart()
|
||||
val eleventh = lines[10].value.trimStart()
|
||||
val twelveth = lines[11].value.trimStart()
|
||||
val thirteenth = lines[12].value.trimStart()
|
||||
val fourteenth = lines[13].value.trimStart()
|
||||
|
||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||
// identical float init
|
||||
mods.add(Modification(lines[7].index, true, null))
|
||||
mods.add(Modification(lines[8].index, true, null))
|
||||
mods.add(Modification(lines[9].index, true, null))
|
||||
mods.add(Modification(lines[10].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var overlappingMods = false
|
||||
/*
|
||||
sta prog8_lib.retval_intermX ; remove
|
||||
sty prog8_lib.retval_intermY ; remove
|
||||
lda prog8_lib.retval_intermX ; remove
|
||||
ldy prog8_lib.retval_intermY ; remove
|
||||
sta A1
|
||||
sty A2
|
||||
*/
|
||||
if(first.isStoreReg() && second.isStoreReg()
|
||||
&& third.isLoadReg() && fourth.isLoadReg()
|
||||
&& fifth.isStoreReg() && sixth.isStoreReg()) {
|
||||
val reg1 = first[2]
|
||||
val reg2 = second[2]
|
||||
val reg3 = third[2]
|
||||
val reg4 = fourth[2]
|
||||
val reg5 = fifth[2]
|
||||
val reg6 = sixth[2]
|
||||
if (reg1 == reg3 && reg1 == reg5 && reg2 == reg4 && reg2 == reg6) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
if(firstvalue.contains("prog8_lib.retval_interm") && secondvalue.contains("prog8_lib.retval_interm")
|
||||
&& firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
overlappingMods = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sta A1
|
||||
sty A2
|
||||
lda A1 ; can be removed
|
||||
ldy A2 ; can be removed if not followed by a branch instuction
|
||||
*/
|
||||
if(!overlappingMods && first.isStoreReg() && second.isStoreReg()
|
||||
&& third.isLoadReg() && fourth.isLoadReg()) {
|
||||
val reg1 = first[2]
|
||||
val reg2 = second[2]
|
||||
val reg3 = third[2]
|
||||
val reg4 = fourth[2]
|
||||
if(reg1==reg3 && reg2==reg4) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
if (!fifth.startsWith('b'))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sta A1
|
||||
sty A2 ; ... or stz
|
||||
lda A1 ; can be removed if not followed by a branch instruction
|
||||
*/
|
||||
if(!overlappingMods && first.isStoreReg() && second.isStoreRegOrZero()
|
||||
&& third.isLoadReg() && !fourth.isBranch()) {
|
||||
val reg1 = first[2]
|
||||
val reg3 = third[2]
|
||||
if(reg1==reg3) {
|
||||
val firstvalue = first.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==thirdvalue) {
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sta A1
|
||||
ldy A1 ; make tay
|
||||
sta A1 ; remove
|
||||
*/
|
||||
if(!overlappingMods && first.startsWith("sta") && second.isLoadReg()
|
||||
&& third.startsWith("sta") && second.length>4) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||
val address = getAddressArg(first, symbolTable)
|
||||
if(address==null || !machine.isIOAddress(address)) {
|
||||
overlappingMods = true
|
||||
val reg2 = second[2]
|
||||
mods.add(Modification(lines[1].index, false, " ta$reg2"))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sta A ; or stz double store, remove this first one
|
||||
sta A ; or stz
|
||||
However, this cannot be done relyably because 'A' could be a constant symbol referring to an I/O address.
|
||||
We can't see that here and would otherwise delete valid double stores.
|
||||
*/
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
|
||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||
// if Y isn't modified in between we can omit the second LDY:
|
||||
// ldy #0
|
||||
// lda (ptr),y
|
||||
// ora #3 ; <-- instruction(s) that don't modify Y
|
||||
// ldy #0 ; <-- can be removed
|
||||
// sta (ptr),y
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFourteen) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
val fifth = lines[4].value.trimStart()
|
||||
val sixth = lines[5].value.trimStart()
|
||||
|
||||
if(first.startsWith("ldy") && second.startsWith("lda") && fourth.startsWith("ldy") && fifth.startsWith("sta")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
val fifthvalue = fifth.substring(4)
|
||||
if("y" !in third && firstvalue==fourthvalue && secondvalue==fifthvalue && secondvalue.endsWith(",y") && fifthvalue.endsWith(",y")) {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
if(first.startsWith("ldy") && second.startsWith("lda") && fifth.startsWith("ldy") && sixth.startsWith("sta")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val fifthvalue = fifth.substring(4)
|
||||
val sixthvalue = sixth.substring(4)
|
||||
if("y" !in third && "y" !in fourth && firstvalue==fifthvalue && secondvalue==sixthvalue && secondvalue.endsWith(",y") && sixthvalue.endsWith(",y")) {
|
||||
mods.add(Modification(lines[4].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeStoreLoadSame(
|
||||
linesByFour: List<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
|
||||
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 "))
|
||||
) {
|
||||
val third = lines[3].value.trimStart()
|
||||
val attemptRemove =
|
||||
if(third.isBranch()) {
|
||||
// a branch instruction follows, we can only remove the load instruction if
|
||||
// another load instruction of the same register precedes the store instruction
|
||||
// (otherwise wrong cpu flags are used)
|
||||
val loadinstruction = second.substring(0, 3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
// no branch instruction follows, we can remove the load instruction
|
||||
val address = getAddressArg(lines[2].value, symbolTable)
|
||||
address==null || !machine.isIOAddress(address)
|
||||
}
|
||||
|
||||
if(attemptRemove) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
else if(first=="pha" && second=="pla" ||
|
||||
first=="phx" && second=="plx" ||
|
||||
first=="phy" && second=="ply" ||
|
||||
first=="php" && second=="plp") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
} else if(first=="pha" && second=="plx") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tax"))
|
||||
} else if(first=="pha" && second=="ply") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tay"))
|
||||
} else if(first=="phx" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txa"))
|
||||
} else if(first=="phy" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tya"))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||
|
||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||
// try to get the constant value address, could return null if it's a symbol instead
|
||||
val loadArg = line.trimStart().substring(3).trim()
|
||||
return when {
|
||||
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
||||
loadArg.startsWith('%') -> loadArg.substring(1).toUIntOrNull(2)
|
||||
loadArg.startsWith('#') -> null
|
||||
loadArg.startsWith('(') -> null
|
||||
loadArg[0].isLetter() -> {
|
||||
val identMatch = identifierRegex.find(loadArg)
|
||||
if(identMatch!=null) {
|
||||
val identifier = identMatch.value
|
||||
when (val symbol = symbolTable.flat[identifier]) {
|
||||
is StConstant -> symbol.value.toUInt()
|
||||
is StMemVar -> symbol.address
|
||||
else -> null
|
||||
}
|
||||
} else null
|
||||
}
|
||||
else -> loadArg.substring(1).toUIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[0].value
|
||||
val second = lines[1].value
|
||||
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
||||
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
||||
|| (" ina" in first || "\tina" in first) && (" dea" in second || "\tdea" in second)
|
||||
|| (" inc a" in first || "\tinc a" in first) && (" dec a" in second || "\tdec a" in second)
|
||||
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
||||
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)
|
||||
|| (" dea" in first || "\tdea" in first) && (" ina" in second || "\tina" in second)
|
||||
|| (" dec a" in first || "\tdec a" in first) && (" inc a" in second || "\tinc a" in second)) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessPushPopStack(linesByFour: List<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)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: List<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
|
||||
}
|
28
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
28
codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.core.Cx16VirtualRegisters
|
||||
import prog8.code.core.RegisterOrPair
|
||||
|
||||
|
||||
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
||||
val order = mutableListOf<Int>()
|
||||
// order is:
|
||||
// 1) cx16 virtual word registers,
|
||||
// 2) paired CPU registers,
|
||||
// 3) single CPU registers (order Y,X,A),
|
||||
// 4) CPU Carry status flag
|
||||
val args = sub.parameters.withIndex()
|
||||
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null }
|
||||
|
||||
cx16regs.forEach { order += it.index }
|
||||
pairedRegs.forEach { order += it.index }
|
||||
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
||||
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
|
||||
rest.forEach { order += it.index }
|
||||
require(order.size==sub.parameters.size)
|
||||
return order
|
||||
}
|
136
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
136
codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Normal file
@ -0,0 +1,136 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
internal class AssemblyProgram(
|
||||
override val name: String,
|
||||
outputDir: Path,
|
||||
private val compTarget: ICompilationTarget) : IAssemblyProgram {
|
||||
|
||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||
private val binFile = outputDir.resolve("$name.bin")
|
||||
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||
private val listFile = outputDir.resolve("$name.list")
|
||||
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
when (compTarget.name) {
|
||||
in setOf("c64", "c128", "cx16", "pet32") -> {
|
||||
// CBM machines .prg generation.
|
||||
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
val outFile = when (options.output) {
|
||||
OutputType.PRG -> {
|
||||
command.add("--cbm-prg")
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
prgFile
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
command.add("--nostart")
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
binFile
|
||||
}
|
||||
else -> throw AssemblyError("invalid output type")
|
||||
}
|
||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
|
||||
}
|
||||
"atari" -> {
|
||||
// Atari800XL .xex generation.
|
||||
|
||||
// TODO are these options okay for atari?
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
||||
"--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
val outFile = when (options.output) {
|
||||
OutputType.XEX -> {
|
||||
command.add("--atari-xex")
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
xexFile
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
command.add("--nostart")
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
binFile
|
||||
}
|
||||
else -> throw AssemblyError("invalid output type")
|
||||
}
|
||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
}
|
||||
else -> throw AssemblyError("invalid compilation target")
|
||||
}
|
||||
|
||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
if (result == 0 && compTarget.name!="atari") {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
}
|
||||
return result==0
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
|
||||
val lines = viceMonListFile.toFile().readLines()
|
||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||
for (line in lines) {
|
||||
if(pattern.matchEntire(line)==null)
|
||||
it.write(line+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBreakpointList() {
|
||||
// builds list of breakpoints, appends to monitor list file
|
||||
val breakpoints = mutableListOf<String>()
|
||||
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
||||
for (line in viceMonListFile.toFile().readLines()) {
|
||||
val match = pattern.matchEntire(line)
|
||||
if (match != null)
|
||||
breakpoints.add("break \$" + match.groupValues[1])
|
||||
}
|
||||
val num = breakpoints.size
|
||||
breakpoints.add(0, "; breakpoint list now follows")
|
||||
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||
breakpoints.add(2, "del")
|
||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||
}
|
||||
}
|
1397
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
Normal file
1397
codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
40
codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt
Normal file
40
codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.IPtSubroutine
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtSub
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
||||
when(this) {
|
||||
is PtAsmSub -> {
|
||||
return returns
|
||||
}
|
||||
is PtSub -> {
|
||||
// for non-asm subroutines, determine the return registers based on the type of the return value
|
||||
return if(returntype==null)
|
||||
emptyList()
|
||||
else {
|
||||
val register = when (returntype!!) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
listOf(Pair(register, returntype!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when(returntype) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
null -> null
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
}
|
@ -1,45 +1,51 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ArrayElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.RegisterOrPair
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.RangeExpr
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.AssemblyError
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.PtForLoop
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtRange
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
internal class ForLoopsAsmGen(
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val zeropage: Zeropage
|
||||
) {
|
||||
|
||||
internal fun translate(stmt: ForLoop) {
|
||||
val iterableDt = stmt.iterable.inferType(program)
|
||||
if(!iterableDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
internal fun translate(stmt: PtForLoop) {
|
||||
val iterableDt = stmt.iterable.type
|
||||
when(stmt.iterable) {
|
||||
is RangeExpr -> {
|
||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||
is PtRange -> {
|
||||
val range = (stmt.iterable as PtRange).toConstantIntegerRange()
|
||||
if(range==null) {
|
||||
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
||||
translateForOverNonconstRange(stmt, iterableDt, stmt.iterable as PtRange)
|
||||
} else {
|
||||
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
||||
translateForOverConstRange(stmt, iterableDt, range)
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||
is PtIdentifier -> {
|
||||
translateForOverIterableVar(stmt, iterableDt, stmt.iterable as PtIdentifier)
|
||||
}
|
||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||
val stepsize=range.step.asConstInteger()!!
|
||||
|
||||
if(stepsize < -1) {
|
||||
val limit = range.to.asConstInteger()
|
||||
if(limit==0)
|
||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||
}
|
||||
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
if (stepsize==1 || stepsize==-1) {
|
||||
@ -48,11 +54,39 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt==DataType.ARRAY_B) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
@ -66,11 +100,39 @@ $modifiedLabel cmp #0 ; modified
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt==DataType.ARRAY_B) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
@ -98,14 +160,15 @@ $modifiedLabel cmp #0 ; modified
|
||||
// words, step 1 or -1
|
||||
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
@ -132,14 +195,15 @@ $modifiedLabel2 cmp #0 ; modified
|
||||
stepsize > 0 -> {
|
||||
|
||||
// (u)words, step >= 2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if (iterableDt == DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
@ -180,42 +244,26 @@ $endLabel""")
|
||||
else -> {
|
||||
|
||||
// (u)words, step <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if(iterableDt==DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
tax
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $endLabel
|
||||
bne $loopLabel
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
pha
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
pla
|
||||
txa
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
lda $varname+1
|
||||
$modifiedLabel sbc #0 ; modified
|
||||
@ -223,7 +271,6 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,12 +280,67 @@ $endLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
// pre-check for end already reached.
|
||||
// 'to' is in AY, do NOT clobber this!
|
||||
if(iterableDt==DataType.ARRAY_W) {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W2 ; to
|
||||
sty P8ZP_SCRATCH_W2+1 ; to
|
||||
lda $fromVar
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
lda $fromVar+1
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cpy $fromVar+1
|
||||
beq +
|
||||
bcc ++
|
||||
bcs $endLabel
|
||||
+ cmp $fromVar
|
||||
bcc +
|
||||
beq +
|
||||
bne $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val decl = ident.targetVarDecl(program)!!
|
||||
val symbol = asmgen.symbolTable.lookup(ident.name)
|
||||
val numElements = when(symbol) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
when(iterableDt) {
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
@ -248,8 +350,8 @@ $endLabel""")
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
@ -258,19 +360,18 @@ $loopLabel lda ${65535.toHex()} ; modified
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
val length = decl.arraysize!!.constIndex()!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
if(length<=255) {
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
@ -281,20 +382,22 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
val length = decl.arraysize!!.constIndex()!! * 2
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
@ -302,7 +405,7 @@ $loopLabel sty $indexVar
|
||||
sta $loopvarName
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
@ -320,13 +423,53 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda ${iterableName}_lsb,y
|
||||
sta $loopvarName
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
@ -338,7 +481,7 @@ $indexVar .byte 0""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
|
||||
if (range.isEmpty() || range.step==0)
|
||||
throw AssemblyError("empty range or step 0")
|
||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||
@ -357,12 +500,12 @@ $indexVar .byte 0""")
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
@ -422,7 +565,7 @@ $loopLabel""")
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
// loop over word range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
@ -436,7 +579,7 @@ $loopLabel""")
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
@ -462,16 +605,16 @@ $loopLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
if (range.last == 255) {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
@ -488,16 +631,16 @@ $endLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
when (range.last) {
|
||||
0 -> {
|
||||
asmgen.out("""
|
||||
@ -525,18 +668,18 @@ $endLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
@ -552,18 +695,18 @@ $loopLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
@ -580,6 +723,9 @@ $loopLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine())
|
||||
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
|
||||
asmgen.assignExpressionToVariable(
|
||||
range.from,
|
||||
asmgen.asmVariableName(stmt.variable),
|
||||
stmt.variable.type)
|
||||
}
|
237
codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
Normal file
237
codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
Normal file
@ -0,0 +1,237 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignment
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
|
||||
|
||||
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
|
||||
translateFunctionCall(stmt)
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||
|
||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
// NOTE: does NOT output the code to deal with the result values!
|
||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol?.astNode as IPtSubroutine
|
||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||
|
||||
if(sub is PtAsmSub) {
|
||||
argumentsViaRegisters(sub, call)
|
||||
if (sub.inline) {
|
||||
// inline the subroutine. (regardless of optimization settings!)
|
||||
// we do this by copying the subroutine's statements at the call site.
|
||||
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||
// (this condition has been enforced by an ast check earlier)
|
||||
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||
} else {
|
||||
asmgen.out(" jsr $subAsmName")
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaRegisters(sub)) {
|
||||
if(sub.parameters.size==1) {
|
||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||
} else {
|
||||
// 2 byte params, second in Y, first in A
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||
asmgen.out(" pha")
|
||||
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
} else {
|
||||
// arguments via variables
|
||||
for(arg in sub.parameters.withIndex().zip(call.args))
|
||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
||||
}
|
||||
asmgen.out(" jsr $subAsmName")
|
||||
}
|
||||
else throw AssemblyError("invalid sub type")
|
||||
|
||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||
}
|
||||
|
||||
|
||||
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
|
||||
return when(arg) {
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if (arg.name == "lsb" || arg.name == "msb")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0])
|
||||
if (arg.name == "mkword")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
|
||||
return !arg.isSimple()
|
||||
}
|
||||
is PtAddressOf -> false
|
||||
is PtIdentifier -> false
|
||||
is PtMachineRegister -> false
|
||||
is PtMemoryByte -> false
|
||||
is PtNumber -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
|
||||
val registersUsed = mutableListOf<RegisterOrStatusflag>();
|
||||
|
||||
fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY}
|
||||
fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY}
|
||||
fun usedY() = registersUsed.any {it.registerOrPair==RegisterOrPair.Y || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.XY}
|
||||
|
||||
if(sub.parameters.size==1) {
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
|
||||
} else {
|
||||
val optimalEvalOrder = asmsub6502ArgsEvalOrder(sub)
|
||||
optimalEvalOrder.forEach {
|
||||
val param = sub.parameters[it]
|
||||
val arg = call.args[it]
|
||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
else if(registersUsed.any {it.statusflag!=null}) {
|
||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||
}
|
||||
else {
|
||||
if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, false)
|
||||
if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(usedX()) asmgen.restoreRegisterStack(CpuRegister.X, true)
|
||||
used
|
||||
}
|
||||
} else {
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) {
|
||||
// pass parameter via a regular variable (not via registers)
|
||||
if(!isArgumentTypeCompatible(value.type, parameter.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
||||
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
||||
}
|
||||
|
||||
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
|
||||
// pass argument via a register parameter
|
||||
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val paramRegister: RegisterOrStatusflag = when(sub) {
|
||||
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
|
||||
is PtSub -> RegisterOrStatusflag(registerOverride!!, null)
|
||||
}
|
||||
val statusflag = paramRegister.statusflag
|
||||
val register = paramRegister.registerOrPair
|
||||
val requiredDt = parameter.value.type
|
||||
if(requiredDt!=value.type) {
|
||||
if(value.type largerThan requiredDt)
|
||||
throw AssemblyError("can only convert byte values to word param types")
|
||||
}
|
||||
if (statusflag!=null) {
|
||||
if(requiredDt!=value.type)
|
||||
throw AssemblyError("for statusflag, byte value is required")
|
||||
if (statusflag == Statusflag.Pc) {
|
||||
// this boolean param needs to be set last, right before the jsr
|
||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||
when(value) {
|
||||
is PtNumber -> {
|
||||
val carrySet = value.number.toInt() != 0
|
||||
asmgen.out(if(carrySet) " sec" else " clc")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
// note: cannot use X register here to store A because it might be used for other arguments
|
||||
asmgen.out("""
|
||||
pha
|
||||
clc
|
||||
lda $sourceName
|
||||
beq +
|
||||
sec
|
||||
+ pla""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" ror a")
|
||||
}
|
||||
}
|
||||
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||
return RegisterOrStatusflag(null, statusflag)
|
||||
}
|
||||
else {
|
||||
// via register or register pair
|
||||
register!!
|
||||
if(requiredDt largerThan value.type) {
|
||||
// we need to sign extend the source, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||
} else {
|
||||
val scope = value.definingISub()
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||
else {
|
||||
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
||||
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
|
||||
}
|
||||
val src = if(value.type in PassByReferenceDatatypes) {
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = sub as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
|
||||
}
|
||||
return RegisterOrStatusflag(register, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
return true
|
||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||
return true
|
||||
|
||||
// we have a special rule for some types.
|
||||
// strings are assignable to UWORD, for example, and vice versa
|
||||
if(argType==DataType.STR && paramType==DataType.UWORD)
|
||||
return true
|
||||
if(argType==DataType.UWORD && paramType == DataType.STR)
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
191
codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt
Normal file
191
codeGenCpu6502/src/prog8/codegen/cpu6502/PostIncrDecrAsmGen.kt
Normal file
@ -0,0 +1,191 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
internal fun translate(stmt: PtPostIncrDecr) {
|
||||
val incr = stmt.operator=="++"
|
||||
val targetIdent = stmt.target.identifier
|
||||
val targetMemory = stmt.target.memory
|
||||
val targetArrayIdx = stmt.target.array
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
when (stmt.target.type) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $what | bne + | inc $what+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $what
|
||||
bne +
|
||||
dec $what+1
|
||||
+ dec $what
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$what | ldy #>$what")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
targetMemory!=null -> {
|
||||
fun incDecViaExprEval() {
|
||||
asmgen.assignExpressionToRegister(targetMemory.address, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerIncDec(address: PtBinaryExpression): Boolean {
|
||||
if(address.operator=="+") {
|
||||
val offset = address.right.asConstInteger()
|
||||
if(offset!=null && offset<256) {
|
||||
// we have @(ptr + 255) ++ , or @(ptr+255)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
ldx #$offset""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if(address.right.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when (val addressExpr = targetMemory.address) {
|
||||
is PtNumber -> {
|
||||
val what = addressExpr.number.toHex()
|
||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val what = asmgen.asmVariableName(addressExpr)
|
||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
if(!tryOptimizedPointerIncDec(addressExpr)) {
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
}
|
||||
targetArrayIdx!=null -> {
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
||||
val elementDt = targetArrayIdx.type
|
||||
val constIndex = targetArrayIdx.index.asConstInteger()
|
||||
if(targetArrayIdx.splitWords) {
|
||||
if(constIndex!=null) {
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb+$constIndex
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb+$constIndex
|
||||
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb,x
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb,x
|
||||
+ dec ${asmArrayvarname}_lsb,x""")
|
||||
}
|
||||
return
|
||||
}
|
||||
if(constIndex!=null) {
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
+ dec $asmArrayvarname,x""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
ldy #>$asmArrayvarname
|
||||
clc
|
||||
adc #<$asmArrayvarname
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.inc_var_f""")
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
820
codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
Normal file
820
codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
Normal file
@ -0,0 +1,820 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
/**
|
||||
* Generates the main parts of the program:
|
||||
* - entry/exit code
|
||||
* - initialization routines
|
||||
* - blocks
|
||||
* - subroutines
|
||||
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
|
||||
*/
|
||||
internal class ProgramAndVarsGen(
|
||||
val program: PtProgram,
|
||||
val options: CompilationOptions,
|
||||
val errors: IErrorReporter,
|
||||
private val symboltable: SymbolTable,
|
||||
private val functioncallAsmGen: FunctionCallAsmGen,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val allocator: VariableAllocator,
|
||||
private val zeropage: Zeropage
|
||||
) {
|
||||
private val compTarget = options.compTarget
|
||||
private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
|
||||
|
||||
internal fun generate() {
|
||||
header()
|
||||
val allBlocks = program.allBlocks()
|
||||
|
||||
if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main")
|
||||
throw AssemblyError("first block should be 'main' or 'p8b_main'")
|
||||
|
||||
if(errors.noErrors()) {
|
||||
program.allBlocks().forEach { block2asm(it) }
|
||||
|
||||
// the global list of all floating point constants for the whole program
|
||||
asmgen.out("; global float constants")
|
||||
for (flt in allocator.globalFloatConsts) {
|
||||
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key)
|
||||
val floatvalue = flt.key
|
||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun header() {
|
||||
val ourName = this.javaClass.name
|
||||
val cpu = when(compTarget.machine.cpu) {
|
||||
CpuType.CPU6502 -> "6502"
|
||||
CpuType.CPU65c02 -> "w65c02"
|
||||
else -> "unsupported"
|
||||
}
|
||||
|
||||
asmgen.out("; $cpu assembly code for '${program.name}'")
|
||||
asmgen.out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
|
||||
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
|
||||
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
||||
asmgen.out("")
|
||||
asmgen.out(".cpu '$cpu'\n.enc 'none'")
|
||||
|
||||
// the global prog8 variables needed
|
||||
val zp = zeropage
|
||||
asmgen.out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
|
||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||
asmgen.out(".endweak")
|
||||
|
||||
if(options.symbolDefs.isNotEmpty()) {
|
||||
asmgen.out("; -- user supplied symbols on the command line")
|
||||
for((name, value) in options.symbolDefs) {
|
||||
asmgen.out("$name = $value")
|
||||
}
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.RAW -> {
|
||||
asmgen.out("; ---- raw assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
}
|
||||
OutputType.PRG -> {
|
||||
when(options.launcher) {
|
||||
CbmPrgLauncherType.BASIC -> {
|
||||
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
||||
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
|
||||
}
|
||||
asmgen.out("; ---- basic program with sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
val year = LocalDate.now().year
|
||||
asmgen.out(" .word (+), $year")
|
||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
asmgen.out("+\t.word 0")
|
||||
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
}
|
||||
|
||||
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
|
||||
asmgen.out("""
|
||||
; zeropage is clobbered so we need to reset the machine at exit
|
||||
lda #>sys.reset_system
|
||||
pha
|
||||
lda #<sys.reset_system
|
||||
pha""")
|
||||
}
|
||||
|
||||
when(compTarget.name) {
|
||||
"cx16" -> {
|
||||
if(options.floats)
|
||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
else -> asmgen.jmp("p8b_main.p8s_start")
|
||||
}
|
||||
}
|
||||
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
for (slab in symboltable.allMemorySlabs) {
|
||||
if (slab.align > 1u)
|
||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
||||
}
|
||||
asmgen.out("\t.bend\n .send slabs_BSS")
|
||||
}
|
||||
}
|
||||
|
||||
private fun tempVars() {
|
||||
asmgen.out("; expression temp vars\n .section BSS")
|
||||
for((dt, count) in asmgen.tempVarsCounters) {
|
||||
if(count>0) {
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
var relocatedBssEnd = 0u
|
||||
|
||||
if(options.varsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.varsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
if(options.slabsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.slabsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
asmgen.out("; bss sections")
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun block2asm(block: PtBlock) {
|
||||
asmgen.out("")
|
||||
asmgen.out("; ---- block: '${block.name}' ----")
|
||||
if(block.options.address!=null)
|
||||
asmgen.out("* = ${block.options.address!!.toHex()}")
|
||||
else {
|
||||
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
|
||||
asmgen.out("\t.align 2")
|
||||
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
|
||||
asmgen.out("\t.align $100")
|
||||
}
|
||||
|
||||
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
|
||||
asmgen.outputSourceLine(block)
|
||||
|
||||
createBlockVariables(block)
|
||||
asmsubs2asm(block.children)
|
||||
|
||||
asmgen.out("")
|
||||
|
||||
val initializers = blockVariableInitializers.getValue(block)
|
||||
val notInitializers = block.children.filterNot { it in initializers }
|
||||
notInitializers.forEach { asmgen.translate(it) }
|
||||
|
||||
// generate subroutine to initialize block-level (global) variables
|
||||
if (initializers.isNotEmpty()) {
|
||||
asmgen.out("prog8_init_vars\t.block")
|
||||
initializers.forEach { assign ->
|
||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
asmgen.translate(assign)
|
||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
|
||||
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||
}
|
||||
|
||||
private fun getVars(scope: StNode): Map<String, StNode> =
|
||||
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
||||
|
||||
private fun createBlockVariables(block: PtBlock) {
|
||||
val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.BLOCK)
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
val mvs = varsInBlock
|
||||
.filter { it.value.type==StNodeType.MEMVAR }
|
||||
.map { it.value as StMemVar }
|
||||
val consts = varsInBlock
|
||||
.filter { it.value.type==StNodeType.CONSTANT }
|
||||
.map { it.value as StConstant }
|
||||
memdefsAndConsts2asm(mvs, consts)
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
internal fun translateAsmSubroutine(sub: PtAsmSub) {
|
||||
if(sub.inline) {
|
||||
return // subroutine gets inlined at call site.
|
||||
}
|
||||
|
||||
asmgen.out("")
|
||||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
asmStartScope = ".proc"
|
||||
asmEndScope = ".pend"
|
||||
}
|
||||
|
||||
if(sub.address!=null)
|
||||
return // already done at the memvars section
|
||||
|
||||
// asmsub with most likely just an inline asm in it
|
||||
asmgen.out("${sub.name}\t$asmStartScope")
|
||||
sub.children.forEach { asmgen.translate(it) }
|
||||
asmgen.out(" $asmEndScope")
|
||||
}
|
||||
|
||||
|
||||
internal fun translateSubroutine(sub: PtSub) {
|
||||
asmgen.out("")
|
||||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
asmStartScope = ".proc"
|
||||
asmEndScope = ".pend"
|
||||
}
|
||||
|
||||
asmgen.out("${sub.name}\t$asmStartScope")
|
||||
|
||||
val scope = symboltable.lookupOrElse(sub.scopedName) {
|
||||
throw AssemblyError("lookup ${sub.scopedName}")
|
||||
}
|
||||
require(scope.type==StNodeType.SUBROUTINE)
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
val mvs = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.MEMVAR }
|
||||
.map { it.value as StMemVar }
|
||||
val consts = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.CONSTANT }
|
||||
.map { it.value as StConstant }
|
||||
memdefsAndConsts2asm(mvs, consts)
|
||||
|
||||
asmsubs2asm(sub.children)
|
||||
|
||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||
asmgen.out("; simple int arg(s) passed via register(s)")
|
||||
if(sub.parameters.size==1) {
|
||||
val dt = sub.parameters[0].type
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||
if(dt in ByteDatatypes)
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
else
|
||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||
} else {
|
||||
require(sub.parameters.size==2)
|
||||
// 2 simple byte args, first in A, second in Y
|
||||
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
|
||||
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.out("; statements")
|
||||
sub.children.forEach { asmgen.translate(it) }
|
||||
|
||||
asmgen.out("; variables")
|
||||
asmgen.out(" .section BSS")
|
||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||
if(addr!=null)
|
||||
asmgen.out("$name = $addr")
|
||||
else when(dt) {
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
asmgen.out(" .send BSS")
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
asmgen.out(" $asmEndScope")
|
||||
}
|
||||
|
||||
private fun entrypointInitialization() {
|
||||
asmgen.out("; program startup initialization")
|
||||
asmgen.out(" cld | tsx | stx prog8_lib.orig_stackpointer ; required for sys.exit()")
|
||||
// set full BSS area to zero
|
||||
asmgen.out("""
|
||||
.if prog8_bss_section_size>0
|
||||
; reset all variables in BSS section to zero
|
||||
lda #<prog8_bss_section_start
|
||||
ldy #>prog8_bss_section_start
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #<prog8_bss_section_size
|
||||
ldy #>prog8_bss_section_size
|
||||
lda #0
|
||||
jsr prog8_lib.memset
|
||||
.endif""")
|
||||
|
||||
blockVariableInitializers.forEach {
|
||||
if (it.value.isNotEmpty())
|
||||
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
||||
}
|
||||
|
||||
// string and array variables in zeropage that have initializer value, should be initialized
|
||||
val stringVarsWithInitInZp = getZpStringVarsWithInitvalue()
|
||||
val arrayVarsWithInitInZp = getZpArrayVarsWithInitvalue()
|
||||
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
|
||||
asmgen.out("; zp str and array initializations")
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val name = asmgen.asmVariableName(it.name)
|
||||
asmgen.out("""
|
||||
lda #<${name}
|
||||
ldy #>${name}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${name}_init_value
|
||||
ldy #>${name}_init_value
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val size = it.alloc.size
|
||||
val name = asmgen.asmVariableName(it.name)
|
||||
asmgen.out("""
|
||||
lda #<${name}_init_value
|
||||
ldy #>${name}_init_value
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
lda #<${name}
|
||||
ldy #>${name}
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
lda #<$size
|
||||
ldy #>$size
|
||||
jsr sys.memcopy""")
|
||||
}
|
||||
asmgen.out(" jmp +")
|
||||
}
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("""+
|
||||
clv
|
||||
clc""")
|
||||
}
|
||||
|
||||
private class ZpStringWithInitial(
|
||||
val name: String,
|
||||
val alloc: MemoryAllocator.VarAllocation,
|
||||
val value: Pair<String, Encoding>
|
||||
)
|
||||
|
||||
private class ZpArrayWithInitial(
|
||||
val name: String,
|
||||
val alloc: MemoryAllocator.VarAllocation,
|
||||
val value: StArray
|
||||
)
|
||||
|
||||
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
|
||||
val result = mutableListOf<ZpStringWithInitial>()
|
||||
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
||||
for (variable in vars) {
|
||||
val scopedName = variable.key
|
||||
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||
if(svar?.onetimeInitializationStringValue!=null)
|
||||
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
|
||||
val result = mutableListOf<ZpArrayWithInitial>()
|
||||
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
||||
for (variable in vars) {
|
||||
val scopedName = variable.key
|
||||
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||
if(svar?.onetimeInitializationArrayValue!=null)
|
||||
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun zeropagevars2asm(varNames: Set<String>) {
|
||||
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }.toList().sortedBy { it.second.address }
|
||||
for ((scopedName, zpvar) in zpVariables) {
|
||||
if (scopedName.startsWith("cx16.r"))
|
||||
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
||||
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||
if(variable.dt in SplitWordArrayTypes) {
|
||||
val lsbAddr = zpvar.address
|
||||
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
|
||||
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
|
||||
asmgen.out("${scopedName.substringAfterLast('.')}_msb \t= $msbAddr \t; zp ${zpvar.dt} (msbs)")
|
||||
} else {
|
||||
asmgen.out("${scopedName.substringAfterLast('.')} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
||||
asmgen.out("")
|
||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||
if(varsNoInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables without initialization value")
|
||||
asmgen.out(" .section BSS")
|
||||
varsNoInit.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
if(varsWithInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables")
|
||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
|
||||
stringvars.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
it.onetimeInitializationStringValue!!.second,
|
||||
it.onetimeInitializationStringValue!!.first
|
||||
)
|
||||
}
|
||||
othervars.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
in SplitWordArrayTypes -> {
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||
val initialValue: Number =
|
||||
if(variable.onetimeInitializationNumericValue!=null) {
|
||||
if(variable.dt== DataType.FLOAT)
|
||||
variable.onetimeInitializationNumericValue!!
|
||||
else
|
||||
variable.onetimeInitializationNumericValue!!.toInt()
|
||||
} else 0
|
||||
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
DataType.FLOAT -> {
|
||||
if(initialValue==0) {
|
||||
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||
} else {
|
||||
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
||||
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||
}
|
||||
}
|
||||
DataType.STR -> {
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
|
||||
when(dt) {
|
||||
DataType.ARRAY_UB -> {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .byte " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.char ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .char " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.word ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .word " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.sint ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .sint " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UW_SPLIT -> {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||
}
|
||||
DataType.ARRAY_W_SPLIT -> {
|
||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
val floatFills = array.map {
|
||||
compTarget.machine.getFloatAsmBytes(it.number!!)
|
||||
}
|
||||
asmgen.out(varname)
|
||||
for (f in array.zip(floatFills))
|
||||
asmgen.out(" .byte ${f.second} ; float ${f.first}")
|
||||
}
|
||||
else -> throw AssemblyError("require array dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
|
||||
memvars.sortedBy { it.address }.forEach {
|
||||
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
||||
}
|
||||
consts.sortedBy { it.name }.forEach {
|
||||
if(it.dt==DataType.FLOAT)
|
||||
asmgen.out(" ${it.name} = ${it.value}")
|
||||
else
|
||||
asmgen.out(" ${it.name} = ${it.value.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun asmsubs2asm(statements: List<PtNode>) {
|
||||
statements
|
||||
.filter { it is PtAsmSub && it.address!=null }
|
||||
.forEach { asmsub ->
|
||||
asmsub as PtAsmSub
|
||||
asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||
for (chunk in outputBytes.chunked(16))
|
||||
asmgen.out(" .byte " + chunk.joinToString())
|
||||
}
|
||||
|
||||
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
return when (dt) {
|
||||
DataType.ARRAY_UB ->
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||
if(it.number!=null) {
|
||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
else if(it.addressOfSymbol!=null) {
|
||||
asmgen.asmSymbolName(it.addressOfSymbol!!)
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird array elt")
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
return when (dt) {
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
DataType.ARRAY_UB ->
|
||||
array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
DataType.ARRAY_B ->
|
||||
array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
||||
if(number>=0)
|
||||
"$$hexnum"
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||
if(number>=0)
|
||||
"$$hexnum"
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
131
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal file
131
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal file
@ -0,0 +1,131 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import com.github.michaelbull.result.onSuccess
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) {
|
||||
|
||||
private val zeropage = options.compTarget.machine.zeropage
|
||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
||||
|
||||
init {
|
||||
allocateZeropageVariables()
|
||||
zeropageVars = zeropage.allocatedVariables
|
||||
}
|
||||
|
||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
||||
|
||||
internal fun getFloatAsmConst(number: Double): String {
|
||||
val asmName = globalFloatConsts[number]
|
||||
if(asmName!=null)
|
||||
return asmName
|
||||
|
||||
val newName = "prog8_float_const_${globalFloatConsts.size}"
|
||||
globalFloatConsts[number] = newName
|
||||
return newName
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate variables into the Zeropage.
|
||||
* The result should be retrieved from the current machine's zeropage object!
|
||||
*/
|
||||
private fun allocateZeropageVariables() {
|
||||
if(options.zeropage== ZeropageType.DONTUSE)
|
||||
return
|
||||
|
||||
val allVariables = collectAllVariables(symboltable)
|
||||
|
||||
val numberOfAllocatableVariables = allVariables.size
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
|
||||
val numberOfExplicitNonZpVariables = allVariables.count { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
var numberOfNonIntegerVariables = 0
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
errors
|
||||
)
|
||||
result.fold(
|
||||
success = {
|
||||
numVariablesAllocatedInZP++
|
||||
},
|
||||
failure = {
|
||||
errors.err(it.message!!, variable.astNode.position)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
// no need to check for allocation error, if there is one, just allocate in normal system ram.
|
||||
}
|
||||
|
||||
// try to allocate any other interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt in IntegerDatatypes) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
}
|
||||
} else
|
||||
numberOfNonIntegerVariables++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||
// println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||
}
|
||||
|
||||
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||
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(st)
|
||||
return vars.sortedBy { it.dt }
|
||||
}
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
//
|
||||
// This contains codegen for stack-based evaluation of binary expressions.
|
||||
// It uses the CPU stack so depth is limited.
|
||||
// It is called "as a last resort" if the optimized codegen path is unable
|
||||
// to come up with a special case of the expression.
|
||||
//
|
||||
internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
|
||||
return assignByteBinExpr(expr, assign)
|
||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
throw AssemblyError("words operands comparison -> byte, should have been handled by assignOptimizedComparisonWords()")
|
||||
}
|
||||
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
"both operands must be words"
|
||||
}
|
||||
return assignWordBinExpr(expr)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
"both operands must be floats"
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> TODO("word + at ${expr.position}")
|
||||
"-" -> TODO("word - at ${expr.position}")
|
||||
"*" -> TODO("word * at ${expr.position}")
|
||||
"/" -> TODO("word / at ${expr.position}")
|
||||
"<<" -> TODO("word << at ${expr.position}")
|
||||
">>" -> TODO("word >> at ${expr.position}")
|
||||
"%" -> TODO("word % at ${expr.position}")
|
||||
"and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> TODO("word and at ${expr.position}")
|
||||
"|" -> TODO("word or at ${expr.position}")
|
||||
"^", "xor" -> TODO("word xor at ${expr.position}")
|
||||
"==" -> TODO("word == at ${expr.position}")
|
||||
"!=" -> TODO("word != at ${expr.position}")
|
||||
"<" -> TODO("word < at ${expr.position}")
|
||||
"<=" -> TODO("word <= at ${expr.position}")
|
||||
">" -> TODO("word > at ${expr.position}")
|
||||
">=" -> TODO("word >= at ${expr.position}")
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
TODO("byte * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("byte / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("byte << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("byte >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("byte % at ${expr.position}")
|
||||
}
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
TODO("byte == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("byte != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("byte < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("byte <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("byte > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("byte >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FADDT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FSUBT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FMULTT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"/" -> {
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FDIVT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_equal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"!=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_notequal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_less_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greater_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_lesseq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greatereq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
else -> TODO("float expression operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
|
||||
when(asmgen.options.compTarget.name) {
|
||||
C64Target.NAME -> {
|
||||
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
|
||||
// this requires some more forced copying around of float values in certain cases
|
||||
if (right.isSimple()) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
|
||||
asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.popFAC2()
|
||||
}
|
||||
}
|
||||
Cx16Target.NAME -> {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
if (!right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
if (!right.isSimple()) asmgen.popFAC1()
|
||||
}
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
LITERALNUMBER,
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||
}
|
||||
|
||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
val datatype: DataType,
|
||||
val scope: IPtSubroutine?,
|
||||
val position: Position,
|
||||
private val variableAsmName: String? = null,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val origAstTarget: PtAssignTarget? = null
|
||||
)
|
||||
{
|
||||
val constArrayIndexValue by lazy { array?.index?.asConstInteger()?.toUInt() }
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
}
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
throw AssemblyError("register must be integer or float type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||
with(target) {
|
||||
when {
|
||||
identifier != null -> {
|
||||
val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen)
|
||||
if (parameter!=null) {
|
||||
val sub = parameter.definingAsmSub()
|
||||
if (sub!=null) {
|
||||
val reg = sub.parameters.single { it.second===parameter }.first
|
||||
if(reg.statusflag!=null)
|
||||
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||
else
|
||||
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, target.position, register=reg.registerOrPair, origAstTarget = this)
|
||||
}
|
||||
}
|
||||
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, target.position, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||
}
|
||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, pos: Position, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
||||
when(registers) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||
RegisterOrPair.FAC1,
|
||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||
}
|
||||
}
|
||||
|
||||
fun isSameAs(left: PtExpression): Boolean =
|
||||
when(kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
val scopedName: String = if('.' in asmVarname)
|
||||
asmVarname
|
||||
else {
|
||||
val scopeName = (scope as? PtNamedNode)?.scopedName
|
||||
if (scopeName == null) asmVarname else "$scopeName.$asmVarname"
|
||||
}
|
||||
left is PtIdentifier && left.name==scopedName
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
val datatype: DataType,
|
||||
private val variableAsmName: String? = null,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val number: PtNumber? = null,
|
||||
val expression: PtExpression? = null
|
||||
)
|
||||
{
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
val cv = value as? PtNumber
|
||||
if(cv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||
is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||
is PtIdentifier -> {
|
||||
val parameter = asmgen.findSubroutineParameter(value.name, asmgen)
|
||||
if(parameter?.definingAsmSub() != null)
|
||||
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
||||
val varName=asmgen.asmVariableName(value)
|
||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||
val regStr = varName.lowercase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
|
||||
} else {
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = varName)
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
||||
}
|
||||
is PtFunctionCall -> {
|
||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||
}
|
||||
else -> {
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
|
||||
// allow some signed/unsigned relaxations
|
||||
|
||||
fun withAdjustedDt(newType: DataType) =
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||
|
||||
if(target.datatype!=datatype) {
|
||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
init {
|
||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsmAssignment(source: AsmAssignSource,
|
||||
target: AsmAssignTarget,
|
||||
memsizer: IMemSizer,
|
||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||
|
||||
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
||||
val operator: String,
|
||||
target: AsmAssignTarget,
|
||||
memsizer: IMemSizer,
|
||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user